diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml new file mode 100644 index 0000000..b05889f --- /dev/null +++ b/.github/workflows/code_quality.yml @@ -0,0 +1,17 @@ +name: Code Quality + +on: + - pull_request + - push + +jobs: + eslint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: npm ci + working-directory: node + - name: Run ESLint + run: npm run lint + working-directory: node diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..1d7ac85 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e01df6a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "prettier.configPath": "./node/.prettierrc", + "prettier.printWidth": 120, + "prettier.tabWidth": 2, + "prettier.trailingComma": "all", + "prettier.semi": true, + "prettier.useTabs": true, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } +} diff --git a/README.md b/README.md index 8458512..928557f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Adventure Land - The Open Source CODE MMORPG + https://adventure.land The Source Code is now available to anyone, even for commercial use! (License applies) @@ -12,9 +13,11 @@ Consider using the #development channel on Discord or messaging me directly on D ## Installation Clone the game files into the adventureland folder: + ```sh git clone https://github.com/kaansoral/adventureland adventureland ``` + Clone a modified copy of Python2 App Engine Local App Server into the python folder along with an initialized database with map files: ```sh @@ -22,6 +25,7 @@ git clone https://github.com/kaansoral/adventureland-appserver appserver ``` Set up secrets files and remember to change secret keys! + ```sh cp adventureland/useful/template.secrets.py adventureland/secrets.py cp adventureland/useful/template.variables.js adventureland/node/variables.js @@ -31,6 +35,7 @@ cp adventureland/useful/template.live_variables.js adventureland/node/live_varia You'll need to download Python2.7 and use the command that comes along with it - it could be ./python or python.exe depending on your setup method Here's the download page: https://www.python.org/downloads/release/python-2718/ For Linux: + ```sh apt update apt upgrade -y @@ -45,16 +50,19 @@ pip2.7 check For MacOS pyenv makes sense as it prevents clash between Python versions and allows local versioning by just adding a .python-version to the folder https://github.com/pyenv/pyenv Make sure to install lxml afterwards: + ```sh pip2.7 install lxml ``` Run the Python2.7 backend - includes an HTTP server, datastore, various utilities - emulates Google App Engine: + ```sh python2.7 appserver/sdk/dev_appserver.py --storage_path=appserver/storage/ --blobstore_path=appserver/storage/blobstore/ --datastore_path=appserver/storage/db.rdbms --host=0.0.0.0 --port=80 adventureland/ --require_indexes --skip_sdk_update_check ``` Set up the NodeJS game server: + ```sh npm install adventureland/node/ ``` @@ -62,14 +70,16 @@ npm install adventureland/node/ Edit the paths in `adventureland/node/variables.js` if you want to run the server from any path Enter the path of the NodeJS server: + ```sh cd adventureland/node ``` Run a server: + ```sh node server.js EU I 8022 -``` +``` You should be able to access the game at: http://0.0.0.0/ @@ -80,12 +90,14 @@ The **useful/** folder has useful commands ## Network Add these to your /etc/hosts file: + ```sh 0.0.0.0 thegame.com 0.0.0.0 www.thegame.com ``` And setup nginx with a conf such as: + ```sh worker_processes 3; @@ -140,6 +152,14 @@ I suggest you go live with this development codebase and approach development in The main problem with traditional MMORPG's is people seeking power and while an offline game can provide this for everyone, an online game cannot. Pretty soon new players will avoid such MMORPG's and existing players will also dwindle. In my opinion there are 2 solutions, you could introduce a decay mechanic to overcome this, or concentrate your innovations and additions to make a more social game rather than a game for power hungry people. Originally I intended to add a lot more social integrations, for example browser notifications integrated to the social hub of the game and easy drop ins to friends playing the game etc. So making a more idle and more social game, with new custom art and a new direction could also be a good option, it's definitely the more serene option that anyone can pursue. +## Code formatting + +This project uses [Eslint](https://eslint.org/) and [Prettier](https://prettier.io/). +For now, this is only used inside the game servers (`/node/` folder). +If you use VSCode, please install the recommended extensions for [eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode). The formatting will be automatically done on save. +If you have suggestions regarding the formatting, feel free to open an issue or discuss it on Discord. [Here is the history](https://discord.com/channels/238332476743745536/1163453037126357074) of the conversation regarding the formatting. +As you might notice, several eslint rules have been marked as warnings instead of errors. This is to allow a smooth and iterative transition. Make sure your new code respects the rules, even the warnings. If you want to submit a PR to fix a specific warning in a specific part of the code, feel free to do so. + ## Freelancers Adventure Land's map designer is available for freelance work or for hire. Contact: markjlacandula@gmail.com diff --git a/node/.eslintrc b/node/.eslintrc new file mode 100644 index 0000000..a806da0 --- /dev/null +++ b/node/.eslintrc @@ -0,0 +1,25 @@ +{ + "extends": ["eslint:recommended", "eslint-config-prettier"], + "env": { + "node": true + }, + "parserOptions": { + "ecmaVersion": 10 + }, + "plugins": ["prettier"], + "rules": { + "no-constant-condition": "off", + "no-dupe-else-if": "off", + "indent": "off", + "no-empty": "warn", + "no-inner-declarations": "warn", + "no-unreachable": "warn", + "no-undef": "warn", + "no-unused-vars": "warn", + "no-redeclare": "warn", + "curly": "error", + "prettier/prettier": "error", + "one-var": ["error", "never"], + "no-sequences": "error" + } +} diff --git a/node/.prettierrc b/node/.prettierrc new file mode 100644 index 0000000..0bb2a47 --- /dev/null +++ b/node/.prettierrc @@ -0,0 +1,10 @@ +{ + "tabWidth": 2, + "singleQuote": false, + "printWidth": 120, + "trailingComma": "all", + "semi": true, + "bracketSpacing": true, + "arrowParens": "always", + "useTabs": true +} \ No newline at end of file diff --git a/node/package-lock.json b/node/package-lock.json index fa7d787..5e7075f 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -7,13 +7,172 @@ "": { "name": "adventureland-server", "version": "1.0.0", - "license": "ISC", + "license": "AdventureLandOnlyUse", "dependencies": { "bytebuffer": "^5.0.1", "protobufjs": "^6.9.0", "range_check": "^1.4.0", "request": "^2.88.2", "socket.io": "^4.0.0" + }, + "devDependencies": { + "eslint": "^8.52.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.1", + "prettier": "^3.0.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, "node_modules/@protobufjs/aspromise": { @@ -98,6 +257,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.0.tgz", "integrity": "sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==" }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -110,6 +275,27 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -125,6 +311,36 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -159,6 +375,12 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -175,6 +397,64 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytebuffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", @@ -186,11 +466,54 @@ "node": ">=0.8" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -202,6 +525,12 @@ "node": ">= 0.8" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "node_modules/cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -227,6 +556,20 @@ "node": ">= 0.10" } }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -254,6 +597,58 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -262,6 +657,18 @@ "node": ">=0.4.0" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -299,6 +706,224 @@ "node": ">=10.0.0" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -317,11 +942,120 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -343,6 +1077,24 @@ "node": ">= 0.12" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -351,6 +1103,59 @@ "assert-plus": "^1.0.0" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -372,6 +1177,15 @@ "node": ">=6" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -386,6 +1200,65 @@ "npm": ">=1.3.7" } }, + "node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, "node_modules/ip6": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/ip6/-/ip6-0.0.4.tgz", @@ -399,21 +1272,156 @@ "node": ">= 0.10" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -424,6 +1432,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -443,6 +1457,49 @@ "node": ">=0.6.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", @@ -451,6 +1508,34 @@ "node": ">=0.6" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -470,11 +1555,41 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -483,6 +1598,33 @@ "node": ">= 0.6" } }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -499,11 +1641,193 @@ "node": ">=0.10.0" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/protobufjs": { "version": "6.11.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", @@ -555,6 +1879,26 @@ "node": ">=0.6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/range_check": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/range_check/-/range_check-1.4.0.tgz", @@ -595,6 +1939,167 @@ "node": ">= 6" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -619,6 +2124,33 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/socket.io": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", @@ -680,6 +2212,100 @@ "node": ">=0.10.0" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -692,6 +2318,12 @@ "node": ">=0.8" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -708,6 +2340,39 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -746,6 +2411,27 @@ "extsprintf": "^1.2.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, "node_modules/ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", @@ -765,9 +2451,128 @@ "optional": true } } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + } + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -850,6 +2655,12 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.0.tgz", "integrity": "sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==" }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -859,6 +2670,19 @@ "negotiator": "0.6.3" } }, + "acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -870,6 +2694,27 @@ "uri-js": "^4.2.2" } }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -898,6 +2743,12 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -911,6 +2762,49 @@ "tweetnacl": "^0.14.3" } }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, "bytebuffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", @@ -919,11 +2813,42 @@ "long": "~3" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -932,6 +2857,12 @@ "delayed-stream": "~1.0.0" } }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -951,6 +2882,17 @@ "vary": "^1" } }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -967,11 +2909,54 @@ "ms": "2.1.2" } }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1003,6 +2988,149 @@ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==" }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + } + }, + "eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + } + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -1018,11 +3146,101 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "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, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "requires": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -1038,6 +3256,18 @@ "mime-types": "^2.1.12" } }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -1046,6 +3276,44 @@ "assert-plus": "^1.0.0" } }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -1060,6 +3328,12 @@ "har-schema": "^2.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -1070,6 +3344,50 @@ "sshpk": "^1.7.0" } }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, "ip6": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/ip6/-/ip6-0.0.4.tgz", @@ -1080,21 +3398,107 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.2.0.tgz", "integrity": "sha512-ku//LD7ie/m9UVGCm9KweBIIHP4mB0maNGvav6Hz778fQCNLQF7iZ+H/UuHuqmjJCHCpA5hw8hOeRKxZl8IlXw==" }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -1105,6 +3509,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -1121,11 +3531,67 @@ "verror": "1.10.0" } }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", "integrity": "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==" }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -1139,16 +3605,54 @@ "mime-db": "1.52.0" } }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -1159,11 +3663,133 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "protobufjs": { "version": "6.11.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", @@ -1206,6 +3832,12 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "range_check": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/range_check/-/range_check-1.4.0.tgz", @@ -1242,6 +3874,106 @@ "uuid": "^3.3.2" } }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + } + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1252,6 +3984,27 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "socket.io": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", @@ -1299,6 +4052,67 @@ "tweetnacl": "~0.14.0" } }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "tough-cookie": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", @@ -1308,6 +4122,12 @@ "punycode": "^2.1.1" } }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -1321,6 +4141,27 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1349,11 +4190,32 @@ "extsprintf": "^1.2.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, "ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "requires": {} + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/node/package.json b/node/package.json index 3267a6b..42a750f 100644 --- a/node/package.json +++ b/node/package.json @@ -5,15 +5,27 @@ "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "node server.js EU I 8120" + "start": "node server.js EU I 8120", + "lint": "eslint --ext .js .", + "lint:fix": "eslint --fix --ext .js ." }, "author": "", "license": "AdventureLandOnlyUse", "dependencies": { - "socket.io": "^4.0.0", - "request": "^2.88.2", - "range_check": "^1.4.0", + "bytebuffer": "^5.0.1", "protobufjs": "^6.9.0", - "bytebuffer": "^5.0.1" - } -} + "range_check": "^1.4.0", + "request": "^2.88.2", + "socket.io": "^4.0.0" + }, + "devDependencies": { + "eslint": "^8.52.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.1", + "prettier": "^3.0.3" + }, + "eslintIgnore": [ + "node_modules/*", + "data.js" + ] +} \ No newline at end of file diff --git a/node/precompute.js b/node/precompute.js index d5d258a..e086d1c 100644 --- a/node/precompute.js +++ b/node/precompute.js @@ -1,27 +1,35 @@ -var is_game=0,is_server=0,is_code=0; -var variables=require('./variables'); -var is_sdk=variables.is_sdk; -var fs=require('fs'); -var precomputed=null; -eval(""+fs.readFileSync(variables.cfunctions_path)); -eval(""+fs.readFileSync(variables.functions_path)); -var mode={},server_id='precompute',server_auth="123456",base_url=variables.base_url; +var is_game = 0; +var is_server = 0; +var is_code = 0; +var variables = require("./variables"); +var is_sdk = variables.is_sdk; +var fs = require("fs"); +var precomputed = null; +eval("" + fs.readFileSync(variables.cfunctions_path)); +eval("" + fs.readFileSync(variables.functions_path)); +var mode = {}; +var server_id = "precompute"; +var server_auth = "123456"; +var base_url = variables.base_url; -appengine_call("reload_server",{keyword:variables.keyword},function(result){ - if(result.failed) return; - G=result.game; - var start=new Date(); - for(var mname in G.maps) - { - if(G.maps[mname].ignore) continue; - var cstart=new Date(); - server_bfs(mname); - console.log("Precomputed: "+mname+" in "+mssince(cstart)+"ms"); +appengine_call("reload_server", { keyword: variables.keyword }, function (result) { + if (result.failed) { + return; } - console.log("Done: "+mssince(start)+"ms"); - var precomputed={}; - precomputed.version=G.version; - precomputed.amap_data=amap_data; - precomputed.smap_data=smap_data; - fs.writeFileSync(variables.data_path,'var precomputed='+JSON.stringify(precomputed)+';'); -}); \ No newline at end of file + G = result.game; + var start = new Date(); + for (var mname in G.maps) { + if (G.maps[mname].ignore) { + continue; + } + var cstart = new Date(); + server_bfs(mname); + console.log("Precomputed: " + mname + " in " + mssince(cstart) + "ms"); + } + console.log("Done: " + mssince(start) + "ms"); + var precomputed = {}; + precomputed.version = G.version; + precomputed.amap_data = amap_data; + precomputed.smap_data = smap_data; + fs.writeFileSync(variables.data_path, "var precomputed=" + JSON.stringify(precomputed) + ";"); +}); diff --git a/node/server.js b/node/server.js index c594c53..9efe6d6 100644 --- a/node/server.js +++ b/node/server.js @@ -1,980 +1,1482 @@ -var is_game=0,is_server=1,is_code=0,is_pvp=false; -var server={ - started:false, - live:false, - last_update:false, - shutdown:false, // shutdown start - stopped:false, // shutdown end - s:{}, +var is_game = 0; +var is_server = 1; +var is_code = 0; +var is_pvp = false; +var server = { + started: false, + live: false, + last_update: false, + shutdown: false, // shutdown start + stopped: false, // shutdown end + s: {}, }; -var variables=require('./variables'); -var is_sdk=variables.is_sdk; -var app=require('http').createServer(http_handler); +var variables = require("./variables"); +var is_sdk = variables.is_sdk; +var app = require("http").createServer(http_handler); //var io=require('socket.io')(app,{pingInterval:2400,pingTimeout:6000}); -var io=require('socket.io')(app,{pingInterval:4000,pingTimeout:12000,cors:{ - origin:false, - methods:"GET,HEAD,PUT,PATCH,POST,DELETE", - credentials:true, - }}); // default is 25000 to 60000 -var fs=require('fs'),url=require('url'); -var {Worker,SHARE_ENV}=require('worker_threads'),workers=[],wlast=0; -eval(""+fs.readFileSync(variables.cfunctions_path)); -eval(""+fs.readFileSync(variables.functions_path)); -eval(""+fs.readFileSync(variables.data_path)); -var base_url=variables.base_url,server_id="1",server_auth="123456",server_name=""; -var players={}; -var dc_players={}; -var sockets={}; -var observers={}; -var total_monsters=0; -var max_players=96; -var chests={}; -var projectiles={}; -var name_to_id={},id_to_id={}; -var invitations={},challenges={},magiportations={},requests={},parties={},frequests={},trequests={}; -var total_moves=1,total_players=0,unique_players=0; -var observer_map="winterland",observer_x=0,observer_y=0; -var merchant_map="main",merchant_y=-50,merchant_x=0,total_merchants=0; -var csold=[],cfound=[]; // cache of S.sold + S.found -var monster_c={}; // monster counts -var pwns=[],pend=0; -var tavern={}; -var dbase={h:8,v:7,vn:2}; // default-base -var G={},D={},P={}; // the game -var S={}; // server data -var T={}; // cosmetics -var E={ - schedule:{ - time_offset:0, - dailies:[13,20], - nightlies:[23], - night:false, +var io = require("socket.io")(app, { + pingInterval: 4000, + pingTimeout: 12000, + cors: { + origin: false, + methods: "GET,HEAD,PUT,PATCH,POST,DELETE", + credentials: true, + }, +}); // default is 25000 to 60000 +var fs = require("fs"); +var url = require("url"); +var { Worker, SHARE_ENV } = require("worker_threads"); +var workers = []; +var wlast = 0; +eval("" + fs.readFileSync(variables.cfunctions_path)); +eval("" + fs.readFileSync(variables.functions_path)); +eval("" + fs.readFileSync(variables.data_path)); +var base_url = variables.base_url; +var server_id = "1"; +var server_auth = "123456"; +var server_name = ""; +var players = {}; +var dc_players = {}; +var sockets = {}; +var observers = {}; +var total_monsters = 0; +var max_players = 96; +var chests = {}; +var projectiles = {}; +var name_to_id = {}; +var id_to_id = {}; +var invitations = {}; +var challenges = {}; +var magiportations = {}; +var requests = {}; +var parties = {}; +var frequests = {}; +var trequests = {}; +var total_moves = 1; +var total_players = 0; +var unique_players = 0; +var observer_map = "winterland"; +var observer_x = 0; +var observer_y = 0; +var merchant_map = "main"; +var merchant_y = -50; +var merchant_x = 0; +var total_merchants = 0; +var csold = []; +var cfound = []; // cache of S.sold + S.found +var monster_c = {}; // monster counts +var pwns = []; +var pend = 0; +var tavern = {}; +var dbase = { h: 8, v: 7, vn: 2 }; // default-base +var G = {}; +var D = {}; +var P = {}; // the game +var S = {}; // server data +var T = {}; // cosmetics +var E = { + schedule: { + time_offset: 0, + dailies: [13, 20], + nightlies: [23], + night: false, }, }; // server event data -var TX=[],TXX={}; // all inter account transactions [07/02/22] -var B={ - "ext_vision":120, // extends the vision by 120 each at all 4 directions -- obsolete now - "u_boundary":70, // when an entity moves 70px in x or y, triggers .u || xy_u_logic, last_u - "u_vision":65, // when an entity moves 65px in x or y, new entities in the new area are sent || xy_upush_logic, last_upush - "vision":[700,500], - "max_vision":1000, // used at the 'attack'/'heal'-'nothtere' logic - "dist":400, - "sell_dist":400, - "door_dist":112, +var TX = []; +var TXX = {}; // all inter account transactions [07/02/22] +var B = { + ext_vision: 120, // extends the vision by 120 each at all 4 directions -- obsolete now + u_boundary: 70, // when an entity moves 70px in x or y, triggers .u || xy_u_logic, last_u + u_vision: 65, // when an entity moves 65px in x or y, new entities in the new area are sent || xy_upush_logic, last_upush + vision: [700, 500], + max_vision: 1000, // used at the 'attack'/'heal'-'nothtere' logic + dist: 400, + sell_dist: 400, + door_dist: 112, // game dynamics - "transporter_dist":160, - "rip_time":12, - "hlevel_loss":3, - "heal_multiplier":1, - "arena_limit":1, - "use_pack_golds":true, - "v":true, - "dps_heal_mult":1.8, // originally 1.6 - "dps_tank_mult":0.25, // originally 0.25 - "game_loop_log_edge":60, - "instance_loop_log_edge":120, - "m_outgoing_gmult":0.12, - "start_map":"main", - "free_last_hits":false, - "pause_instances":true, - "global_drops":true, + transporter_dist: 160, + rip_time: 12, + hlevel_loss: 3, + heal_multiplier: 1, + arena_limit: 1, + use_pack_golds: true, + v: true, + dps_heal_mult: 1.8, // originally 1.6 + dps_tank_mult: 0.25, // originally 0.25 + game_loop_log_edge: 60, + instance_loop_log_edge: 120, + m_outgoing_gmult: 0.12, + start_map: "main", + free_last_hits: false, + pause_instances: true, + global_drops: true, }; -var CC={ - "auth":2, - "move":1.5, - "players":12, - "secondhands":16, - "friend":24, - "send_updates":12, - "cruise":10, - "random_look":10, - "equip":3, - "unequip":6, - "tracker":50, - "ccreport":3, +var CC = { + auth: 2, + move: 1.5, + players: 12, + secondhands: 16, + friend: 24, + send_updates: 12, + cruise: 10, + random_look: 10, + equip: 3, + unequip: 6, + tracker: 50, + ccreport: 3, }; -var limits={ - "calls":200, // 4 seconds - "party":9, - "party_max":10, +var limits = { + calls: 200, // 4 seconds + party: 9, + party_max: 10, }; -var W={ // warning flags - "chest":{}, +var W = { + // warning flags + chest: {}, }; -var mode={ - "range_test":0, - "path_checks":1, // when pursuing a player, monsters check each x/y-line, stop pursuit on collision [14/08/16] +var mode = { + range_test: 0, + path_checks: 1, // when pursuing a player, monsters check each x/y-line, stop pursuit on collision [14/08/16] //#IDEA: Disable when low performance is detected [14/08/16] - "upush_test":0, // stops monsters to test the "push" logic - "random_attacks":0, // a test mode that makes monsters attack nearby players randomly [11/09/16] - "aggro":1, - "dpvpblock":1, // double-sided pvp blocks - "novi":1, // no vision improvement - added as a patch [12/01/17] - "xyinf":0, //sends xy data for corrections - "log_pvp":1, //sends pvp data to appengine, mainly to see whether people leech xp from sub-characters [03/02/17] - "drop_all":0, - "red_zone":1, - "lcorrection":1, - "enforce_smap":1, - "rbugs":0, - "noxy":0, - "nopush":0, - "freeze_latest":0, // to test latest_calls numbers - "log_all":0, // logs all incoming websocket calls - "friendly_fire":1, - "fast_mlevels":0, - "map_respawns":0, // respawn at maps or where they define [05/12/18] - "low49_20xglobal":1, // 20x global drops for <=49 level players [03/02/19] - "low49_200xgoo":1, - "pve_safe_magiports":1, - "instant_monster_attacks":1, // #TODO: Consider dynamically sending target data instantly too - "drm_check":1, - "all_roam":0, - "all_smart":1, - "prevent_external":0, // for "test" / "hardcore" - "pvp_level_gap":0, // have to be within 10 level to attack + upush_test: 0, // stops monsters to test the "push" logic + random_attacks: 0, // a test mode that makes monsters attack nearby players randomly [11/09/16] + aggro: 1, + dpvpblock: 1, // double-sided pvp blocks + novi: 1, // no vision improvement - added as a patch [12/01/17] + xyinf: 0, //sends xy data for corrections + log_pvp: 1, //sends pvp data to appengine, mainly to see whether people leech xp from sub-characters [03/02/17] + drop_all: 0, + red_zone: 1, + lcorrection: 1, + enforce_smap: 1, + rbugs: 0, + noxy: 0, + nopush: 0, + freeze_latest: 0, // to test latest_calls numbers + log_all: 0, // logs all incoming websocket calls + friendly_fire: 1, + fast_mlevels: 0, + map_respawns: 0, // respawn at maps or where they define [05/12/18] + low49_20xglobal: 1, // 20x global drops for <=49 level players [03/02/19] + low49_200xgoo: 1, + pve_safe_magiports: 1, + instant_monster_attacks: 1, // #TODO: Consider dynamically sending target data instantly too + drm_check: 1, + all_roam: 0, + all_smart: 1, + prevent_external: 0, // for "test" / "hardcore" + pvp_level_gap: 0, // have to be within 10 level to attack }; -var events={ +var events = { // SEASONS - "holidayseason":false, - "lunarnewyear":false, - "valentines":false, - "pinkgoo":0, // every N minutes - 60 - "snowman":20*60, // 1200 normally - 60 - at sprocess_game_data - "egghunt":0, // every N minutes - 60 + holidayseason: false, + lunarnewyear: false, + valentines: false, + pinkgoo: 0, // every N minutes - 60 + snowman: 20 * 60, // 1200 normally - 60 - at sprocess_game_data + egghunt: 0, // every N minutes - 60 // RANDOM - "halloween":true, - "goblin":false, - "goldenbat":160000, - "cutebee":960000, - "hide_and_seek":0, + halloween: true, + goblin: false, + goldenbat: 160000, + cutebee: 960000, + hide_and_seek: 0, // DAILIES - "goobrawl":false, - "crabxx":false, - "abtesting":false, + goobrawl: false, + crabxx: false, + abtesting: false, // NIGHTLIES - "icegolem":false, - "franky":false, + icegolem: false, + franky: false, }; -var dailies=["crabxx","goobrawl","abtesting"]; shuffle(dailies); -var nightlies=["icegolem","franky"]; shuffle(nightlies); -if(events.holidayseason) events.snowman=60; -if(events.valentines) events.pinkgoo=60; -TIMEO={ - "EU":1, - "US":-5, - "ASIA":7, +var dailies = ["crabxx", "goobrawl", "abtesting"]; +shuffle(dailies); +var nightlies = ["icegolem", "franky"]; +shuffle(nightlies); +if (events.holidayseason) { + events.snowman = 60; } -var perfc={ - "game_loop":{}, - "instance_loop":{}, - "instance_delay":{}, - "cps":0, - "sxyu":0, - "game_loops":0, - "instance_loops":0, - "roams":0, - "roam_ops":0, +if (events.valentines) { + events.pinkgoo = 60; } -var instances={}; +TIMEO = { + EU: 1, + US: -5, + ASIA: 7, +}; +var perfc = { + game_loop: {}, + instance_loop: {}, + instance_delay: {}, + cps: 0, + sxyu: 0, + game_loops: 0, + instance_loops: 0, + roams: 0, + roam_ops: 0, +}; +var instances = {}; // at first there were no instances, monsters were global // instances improve performance and allow things like dynamic events, dungeons -var luckm=1,xpm=1,goldm=1,buym=1; +var luckm = 1; +var xpm = 1; +var goldm = 1; +var buym = 1; +region = process.argv[process.argv.length - 3]; +server_name = process.argv[process.argv.length - 2]; +port = process.argv[process.argv.length - 1]; -region=process.argv[process.argv.length-3]; -server_name=process.argv[process.argv.length-2]; -port=process.argv[process.argv.length-1]; +E.schedule.time_offset = TIMEO[region]; -E.schedule.time_offset=TIMEO[region]; - -if(server_name=="HARDCORE") -{ - gameplay="hardcore"; - xpm=2188; +if (server_name == "HARDCORE") { + gameplay = "hardcore"; + xpm = 2188; // luckm=500; - goldm=12000; - is_pvp=true; - variables.ip_limit=1; - variables.character_limit=1; - B.rip_time=-1; - B.heal_multiplier=0.6; - B.sell_dist=9999999+1000; - events.golden_bat=8000; - B.arena_limit=1; - B.v=false; - B.free_last_hits=true; - mode.prevent_external=1; - mode.pvp_level_gap=1; + goldm = 12000; + is_pvp = true; + variables.ip_limit = 1; + variables.character_limit = 1; + B.rip_time = -1; + B.heal_multiplier = 0.6; + B.sell_dist = 9999999 + 1000; + events.golden_bat = 8000; + B.arena_limit = 1; + B.v = false; + B.free_last_hits = true; + mode.prevent_external = 1; + mode.pvp_level_gap = 1; +} else if (server_name == "TEST") { + gameplay = "test"; + is_pvp = true; + variables.ip_limit = 5; + variables.character_limit = 5; + B.rip_time = -1; + B.sell_dist = 9999999 + 1000; + B.arena_limit = 1; + B.rbugs = 1; + mode.prevent_external = 1; +} else if (server_name == "DUNGEON") { + gameplay = "dungeon"; + is_pvp = true; + variables.ip_limit = 1; + variables.character_limit = 1; + B.start_map = observer_map = "d_e"; + B.free_last_hits = true; + B.pause_instances = false; + for (var name in events) { + if (is_number(events[name])) { + events[name] = (events[name] !== 0 && 9999999999) || 0; + } else { + events[name] = false; + } + } +} else { + gameplay = "normal"; } -else if(server_name=="TEST") -{ - gameplay="test"; - is_pvp=true; - variables.ip_limit=5; - variables.character_limit=5; - B.rip_time=-1; - B.sell_dist=9999999+1000; - B.arena_limit=1; - B.rbugs=1; - mode.prevent_external=1; -} -else if(server_name=="DUNGEON") -{ - gameplay="dungeon"; - is_pvp=true; - variables.ip_limit=1; - variables.character_limit=1; - B.start_map=observer_map="d_e"; - B.free_last_hits=true; - B.pause_instances=false; - for(var name in events) - if(is_number(events[name])) events[name]=events[name]!==0&&9999999999||0; - else events[name]=false; -} -else gameplay="normal"; // B.u_boundary=12; B.u_vision=12; B["vision"]=[320,270] // B.vision[0]*=100; B.vision[1]*=100; -if(server_name.startsWith("PVP") || server_name.startsWith("HARDCORE")) -{ - is_pvp=true; +if (server_name.startsWith("PVP") || server_name.startsWith("HARDCORE")) { + is_pvp = true; // variables.ip_limit=2+1; // variables.character_limit=1+1; - luckm*=1.15; - xpm*=1.20; - goldm*=1.25; + luckm *= 1.15; + xpm *= 1.2; + goldm *= 1.25; } // var secure_app=require('https').createServer(s_options,https_handler); #GTODO: Implement secure communications at one point -function init_game() -{ - appengine_call("create_server",{keyword:variables.keyword,port:port,region:region,name:server_name,pvp:is_pvp||'',gameplay:gameplay},function(result){ - try{ - if(result.exists) return [console.log("Server Exists!"),process.exit()]; - // console.log(result); - server_id=result.id; - server_auth=result.auth; - server_name=result.name; - server_log("Server Live: "+server_name+" "+server_id,1); - server_log("Node Version: "+process.version,1); - //server_log("Socket.IO Version: "+require("socket.io").version); - S=result.data; - G=result.game; - server_log("Game Version: "+G.version,1); - D=result.dynamics; - if(S) init_server_data(S); - if(G) sprocess_game_data(); - try{ - app.listen(port); - init_io(); - // app2.listen(parseInt(port)+40); - } - catch(e){ - server.exists=true; - server.started=true; - log_trace("port",e); - return shutdown_routine(); - } - //create_instance("main2","main2"); - var start=new Date(); - if(gameplay=="normal" || gameplay=="hardcore" || gameplay=="test") - { - create_instance("main"); - create_instance("tunnel"); - create_instance("cave"); - create_instance("tavern"); - create_instance("resort_e"); create_instance("resort"); - create_instance("mansion"); - create_instance("woffice"); - create_instance("halloween"); - create_instance("spookytown"); - if(gameplay=="normal") create_instance("test"); - create_instance("arena","arena",{pvp:1}); - create_instance("bank"); - create_instance("bank_b"); - create_instance("bank_u"); - //create_instance("batcave","batcave"); - create_instance("winterland"); - create_instance("winter_inn"); create_instance("winter_inn_rooms"); - create_instance("winter_cave"); create_instance("winter_cove"); - create_instance("desertland"); - create_instance("level1"); - create_instance("level2"); - create_instance("level2n"); - create_instance("level2e"); - create_instance("level2s"); - create_instance("level2w"); - create_instance("level3"); - create_instance("level4"); - create_instance("cyberland"); - create_instance("hut"); - create_instance("mtunnel"); - create_instance("ship0"); - create_instance("goobrawl"); - create_instance("d_e"); create_instance("d_g"); create_instance("d_b1"); create_instance("d_a1"); create_instance("d_a2"); - server_bfs("crypt"); - server_bfs("winter_instance"); - server_bfs("tomb"); - server_bfs("dungeon0"); - server_bfs("cgallery"); - } - else if(gameplay=="dungeon") - { - for(var name in G.maps) - if(G.maps[name].world=="dungeon") - create_instance(name); - } - create_instance("jail"); - console.log("Calculations took: "+mssince(start)+"ms"); - shuffle(hiding_places); - init_tavern(); - init_server(); - server.started=true; - server.live=true; - server.last_update=new Date(); - } - catch(e) +function init_game() { + appengine_call( + "create_server", { - log_trace("init",e); - } - },function(){ - console.log("#X init_game's create_server call failed"); - }); + keyword: variables.keyword, + port: port, + region: region, + name: server_name, + pvp: is_pvp || "", + gameplay: gameplay, + }, + function (result) { + try { + if (result.exists) { + return [console.log("Server Exists!"), process.exit()]; + } + // console.log(result); + server_id = result.id; + server_auth = result.auth; + server_name = result.name; + server_log("Server Live: " + server_name + " " + server_id, 1); + server_log("Node Version: " + process.version, 1); + //server_log("Socket.IO Version: "+require("socket.io").version); + S = result.data; + G = result.game; + server_log("Game Version: " + G.version, 1); + D = result.dynamics; + if (S) { + init_server_data(S); + } + if (G) { + sprocess_game_data(); + } + try { + app.listen(port); + init_io(); + // app2.listen(parseInt(port)+40); + } catch (e) { + server.exists = true; + server.started = true; + log_trace("port", e); + return shutdown_routine(); + } + //create_instance("main2","main2"); + var start = new Date(); + if (gameplay == "normal" || gameplay == "hardcore" || gameplay == "test") { + create_instance("main"); + create_instance("tunnel"); + create_instance("cave"); + create_instance("tavern"); + create_instance("resort_e"); + create_instance("resort"); + create_instance("mansion"); + create_instance("woffice"); + create_instance("halloween"); + create_instance("spookytown"); + if (gameplay == "normal") { + create_instance("test"); + } + create_instance("arena", "arena", { pvp: 1 }); + create_instance("bank"); + create_instance("bank_b"); + create_instance("bank_u"); + //create_instance("batcave","batcave"); + create_instance("winterland"); + create_instance("winter_inn"); + create_instance("winter_inn_rooms"); + create_instance("winter_cave"); + create_instance("winter_cove"); + create_instance("desertland"); + create_instance("level1"); + create_instance("level2"); + create_instance("level2n"); + create_instance("level2e"); + create_instance("level2s"); + create_instance("level2w"); + create_instance("level3"); + create_instance("level4"); + create_instance("cyberland"); + create_instance("hut"); + create_instance("mtunnel"); + create_instance("ship0"); + create_instance("goobrawl"); + create_instance("d_e"); + create_instance("d_g"); + create_instance("d_b1"); + create_instance("d_a1"); + create_instance("d_a2"); + server_bfs("crypt"); + server_bfs("winter_instance"); + server_bfs("tomb"); + server_bfs("dungeon0"); + server_bfs("cgallery"); + } else if (gameplay == "dungeon") { + for (var name in G.maps) { + if (G.maps[name].world == "dungeon") { + create_instance(name); + } + } + } + create_instance("jail"); + console.log("Calculations took: " + mssince(start) + "ms"); + shuffle(hiding_places); + init_tavern(); + init_server(); + server.started = true; + server.live = true; + server.last_update = new Date(); + } catch (e) { + log_trace("init", e); + } + }, + function () { + console.log("#X init_game's create_server call failed"); + }, + ); } init_game(); -function reload_server(to_broadcast,change) -{ - appengine_call("reload_server",{keyword:variables.keyword,port:port,region:region,pvp:is_pvp||'',gameplay:gameplay},function(result){ - try{ - G=result.game; - D=result.dynamics; - sprocess_game_data(); - prop_cache={}; - if(to_broadcast) broadcast("reloaded",{change:change||""}); - } - catch(e) - { - broadcast("notice",{message:"Server Live Reload Failed"}); - log_trace("#X live_reload",e); - } - },function(){ - // setTimeout(init_game,1000); - server_log("#X live_reload failed",1); - broadcast("notice",{message:"Live Reload Failed"}); - }); +function reload_server(to_broadcast, change) { + appengine_call( + "reload_server", + { keyword: variables.keyword, port: port, region: region, pvp: is_pvp || "", gameplay: gameplay }, + function (result) { + try { + G = result.game; + D = result.dynamics; + sprocess_game_data(); + prop_cache = {}; + if (to_broadcast) { + broadcast("reloaded", { change: change || "" }); + } + } catch (e) { + broadcast("notice", { message: "Server Live Reload Failed" }); + log_trace("#X live_reload", e); + } + }, + function () { + // setTimeout(init_game,1000); + server_log("#X live_reload failed", 1); + broadcast("notice", { message: "Live Reload Failed" }); + }, + ); } -function decode_http_data(data) -{ +function decode_http_data(data) { // #TODO: Find a better way to transfer data [28/09/17] - var result=decodeURIComponent(data).replace_all("u'","'").replace_all("'",'"').replace_all("+"," ").replace_all("%2B","+"); + var result = decodeURIComponent(data) + .replace_all("u'", "'") + .replace_all("'", '"') + .replace_all("+", " ") + .replace_all("%2B", "+"); return result; } -function parse_http_json(data) -{ - var result=decode_http_data(data),json={}; - try{ - json=JSON.parse(result); - } - catch(e){ - console.log("\n"+data); - log_trace("parse_http_json",e); +function parse_http_json(data) { + var result = decode_http_data(data); + var json = {}; + try { + json = JSON.parse(result); + } catch (e) { + console.log("\n" + data); + log_trace("parse_http_json", e); } return json; } -function http_handler(request,response) -{ +function http_handler(request, response) { var body = []; - request.on('error', function(err) { - server_log("http_err: "+err,1); - }).on('data', function(chunk) { - body.push(chunk); - }).on('end', function() { - body = Buffer.concat(body).toString(); - try{ - // server_log("http_handle's end"); - var url_parts = url.parse(request.url,true),args=url_parts.query,output=""; - //console.log(body); - (body||"").split("&").forEach(function(pv){ - var pvp=pv.split("="); - args[pvp[0]]=pvp[1]; - }); - if(args.checkin) - { - // console.log(JSON.stringify(args)); - // safe_search(request,"::1"); - // console.log(JSON.stringify(request.headers)); - console.log("start"); req=request; - console.log(req.connection ? req.connection.remoteAddress : null); - console.log(req.socket ? req.socket.remoteAddress : null); - console.log((req.connection && req.connection.socket) ? req.connection.socket.remoteAddress : null); - console.log(req.info ? req.info.remoteAddress : null); - var ip=getClientIp(request),id=id_to_id[args.id]; - // console.log(ip); - if(players[id] && players[id].ipass==args.ipass) - { - players[id].last_ip=ip; - players[id].last_ipass=new Date(); - // server_log("ipass for "+players[id].name); + request + .on("error", function (err) { + server_log("http_err: " + err, 1); + }) + .on("data", function (chunk) { + body.push(chunk); + }) + .on("end", function () { + body = Buffer.concat(body).toString(); + try { + // server_log("http_handle's end"); + var url_parts = url.parse(request.url, true); + var args = url_parts.query; + var output = ""; + //console.log(body); + (body || "").split("&").forEach(function (pv) { + var pvp = pv.split("="); + args[pvp[0]] = pvp[1]; + }); + if (args.checkin) { + // console.log(JSON.stringify(args)); + // safe_search(request,"::1"); + // console.log(JSON.stringify(request.headers)); + console.log("start"); + req = request; + console.log(req.connection ? req.connection.remoteAddress : null); + console.log(req.socket ? req.socket.remoteAddress : null); + console.log(req.connection && req.connection.socket ? req.connection.socket.remoteAddress : null); + console.log(req.info ? req.info.remoteAddress : null); + var ip = getClientIp(request); + var id = id_to_id[args.id]; + // console.log(ip); + if (players[id] && players[id].ipass == args.ipass) { + players[id].last_ip = ip; + players[id].last_ipass = new Date(); + // server_log("ipass for "+players[id].name); + } + } + if (args.spass != variables.access_master) { + response.writeHead(200); + response.end(output); + return; + } + if (args.aevent == "shutdown") { + shutdown_routine(); + } + if (args.aevent == "cupdate") { + var id = id_to_id[args.id]; + server_log("cupdate for " + args.id + " socket.id: " + id); + if (players[id]) { + var player = players[id]; + player.cash = args.cash; + if (args.ncash && args.ncash != "0") { + player.socket.emit("game_log", { message: "Received " + args.ncash + " shells", color: colors.cash }); + } + + resend(player, "reopen+nc"); + } + output = "yes"; + } + if (args.aevent == "new_friend") { + var id = id_to_id[args.id]; + server_log("new_friend for " + args.id + " socket.id: " + id, 1); + if (players[id]) { + var player = players[id]; + player.friends = parse_http_json(args.friends); + player.socket.emit("friend", { event: "new", name: args.name, friends: player.friends }); + resend(player, "redata"); + } + output = "yes"; + } + if (args.aevent == "lost_friend") { + var id = id_to_id[args.id]; + server_log("lost_friend for " + args.id + " socket.id: " + id, 1); + if (players[id]) { + var player = players[id]; + player.friends = parse_http_json(args.friends); + player.socket.emit("friend", { event: "lost", friends: player.friends }); // ,name:args.name + resend(player, "redata"); + } + output = "yes"; + } + if (args.aevent == "eval") { + var data = parse_http_json(args.data); + try { + eval(decode_http_data(args.code)); + } catch (e) { + console.log("\n" + args.code); + log_trace("chttp_eval", e); + } + output = JSON.stringify(output); } - } - if(args.spass!=variables.access_master) - { response.writeHead(200); response.end(output); - return; + } catch (e) { + log_trace("chttp_err", e); } - if(args.aevent=="shutdown") - { - shutdown_routine(); - } - if(args.aevent=="cupdate") - { - var id=id_to_id[args.id]; - server_log("cupdate for "+args.id+" socket.id: "+id); - if(players[id]) - { - var player=players[id]; - player.cash=args.cash; - if(args.ncash && args.ncash!="0") player.socket.emit("game_log",{message:"Received "+args.ncash+" shells",color:colors.cash}); - - resend(player,"reopen+nc"); - } - output="yes"; - } - if(args.aevent=="new_friend") - { - var id=id_to_id[args.id]; - server_log("new_friend for "+args.id+" socket.id: "+id,1); - if(players[id]) - { - var player=players[id]; - player.friends=parse_http_json(args.friends); - player.socket.emit("friend",{event:"new",name:args.name,friends:player.friends}); - resend(player,"redata"); - } - output="yes"; - } - if(args.aevent=="lost_friend") - { - var id=id_to_id[args.id]; - server_log("lost_friend for "+args.id+" socket.id: "+id,1); - if(players[id]) - { - var player=players[id]; - player.friends=parse_http_json(args.friends); - player.socket.emit("friend",{event:"lost",friends:player.friends}); // ,name:args.name - resend(player,"redata"); - } - output="yes"; - } - if(args.aevent=="eval") - { - var data=parse_http_json(args.data); - try{ - eval(decode_http_data(args.code)); - } - catch(e){ - console.log("\n"+args.code); - log_trace("chttp_eval",e); - } - output=JSON.stringify(output); - } - response.writeHead(200); - response.end(output); - }catch(e){ - log_trace("chttp_err",e); - } - }); + }); } -function player_to_server(player,place) -{ - var char={}; - for(prop in player) - if(!in_arr(prop,["auth","last_sync","socket","character","last_upush","push","last","last_u","width","height","u"])) - char[prop]=player[prop]; - if(place=="sync" && !Object.keys(player.q).length && player.type!="merchant") delete char.p; // ~20KB - too much [19/11/18] +function player_to_server(player, place) { + var char = {}; + for (prop in player) { + if ( + !in_arr(prop, [ + "auth", + "last_sync", + "socket", + "character", + "last_upush", + "push", + "last", + "last_u", + "width", + "height", + "u", + ]) + ) { + char[prop] = player[prop]; + } + } + if (place == "sync" && !Object.keys(player.q).length && player.type != "merchant") { + delete char.p; + } // ~20KB - too much [19/11/18] return char; } -function player_to_client(player,stranger) -{ - var data={}; - ["hp","max_hp","mp","max_mp","xp","attack","heal","frequency","speed","range","armor","resistance","level","party", - "rip","npc","allow","code","afk","target","focus","role","s","c","q","b","age","pdps", - "id","x","y","moving","going_x","going_y","abs","move_num","angle","cid","guild","team"].forEach(function(p){ - // removed "vx","vy" - if(player[p]!==undefined) data[p]=player[p]; +function player_to_client(player, stranger) { + var data = {}; + [ + "hp", + "max_hp", + "mp", + "max_mp", + "xp", + "attack", + "heal", + "frequency", + "speed", + "range", + "armor", + "resistance", + "level", + "party", + "rip", + "npc", + "allow", + "code", + "afk", + "target", + "focus", + "role", + "s", + "c", + "q", + "b", + "age", + "pdps", + "id", + "x", + "y", + "moving", + "going_x", + "going_y", + "abs", + "move_num", + "angle", + "cid", + "guild", + "team", + ].forEach(function (p) { + // removed "vx","vy" + if (player[p] !== undefined) { + data[p] = player[p]; + } }); - ["stand"].forEach(function(p){ - if(player.p && player.p[p]!==undefined) data[p]=player.p[p]; + ["stand"].forEach(function (p) { + if (player.p && player.p[p] !== undefined) { + data[p] = player.p[p]; + } }); - if(player.afk=="code") data.controller=player.controller; - if(player.rip && (player.tcx&&player.tcx.gravestone||player.cx.gravestone)) player.rip=(player.tcx&&player.tcx.gravestone||player.cx.gravestone); - data.skin=player.tskin||player.skin; - data.cx=player.tcx||player.cx; - data.slots=player.cslots; - if(player.tp) data.tp=true; - data.ctype=player.type; - data.owner=!player.private&&player.owner||""; + if (player.afk == "code") { + data.controller = player.controller; + } + if (player.rip && ((player.tcx && player.tcx.gravestone) || player.cx.gravestone)) { + player.rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone; + } + data.skin = player.tskin || player.skin; + data.cx = player.tcx || player.cx; + data.slots = player.cslots; + if (player.tp) { + data.tp = true; + } + data.ctype = player.type; + data.owner = (!player.private && player.owner) || ""; - if(player.is_npc) - { + if (player.is_npc) { // data.id="$"+data.id; - data.name=player.name; - if(player.direction!==undefined) data.direction=player.direction,data.npc=player.npc; + data.name = player.name; + if (player.direction !== undefined) { + data.direction = player.direction; + data.npc = player.npc; + } } - if(!stranger) - { - ["int","str","dex","vit","for","mp_cost","mp_reduction","max_xp","goldm","xpm","luckm","map","in","isize","esize","gold","cash","targets","m","evasion","miss","reflection","lifesteal","manasteal","rpiercing","apiercing","crit","critdamage","dreturn","tax","xrange","pnresistance","firesistance","fzresistance","phresistance","stresistance","incdmgamp","stun","blast","explosion","courage","mcourage","pcourage","fear"].forEach(function(p){ //"vision", - data[p]=player[p]; + if (!stranger) { + [ + "int", + "str", + "dex", + "vit", + "for", + "mp_cost", + "mp_reduction", + "max_xp", + "goldm", + "xpm", + "luckm", + "map", + "in", + "isize", + "esize", + "gold", + "cash", + "targets", + "m", + "evasion", + "miss", + "reflection", + "lifesteal", + "manasteal", + "rpiercing", + "apiercing", + "crit", + "critdamage", + "dreturn", + "tax", + "xrange", + "pnresistance", + "firesistance", + "fzresistance", + "phresistance", + "stresistance", + "incdmgamp", + "stun", + "blast", + "explosion", + "courage", + "mcourage", + "pcourage", + "fear", + ].forEach(function (p) { + //"vision", + data[p] = player[p]; }); - data.items=player.citems; - if(player.user!==undefined) - { - data.user=player.cuser; - if(player.user) data.user.gold=player.user.gold; + data.items = player.citems; + if (player.user !== undefined) { + data.user = player.cuser; + if (player.user) { + data.user.gold = player.user.gold; + } + } + if (player.socket) { + data.cc = get_call_cost(player); } - if(player.socket) data.cc=get_call_cost(player); } return data; } -function monster_to_client(monster,events) -{ - var data={},def=G.monsters[monster.type]; - ["speed","hp","mp","max_mp","attack","xp","frequency","armor","resistance","1hp","skin","cooperative","drops"].forEach(function(p){ //same array as game.js adopt_soft_properties - if(p in monster && monster[p]!=def[p]) data[p]=monster[p]; +function monster_to_client(monster, events) { + var data = {}; + var def = G.monsters[monster.type]; + [ + "speed", + "hp", + "mp", + "max_mp", + "attack", + "xp", + "frequency", + "armor", + "resistance", + "1hp", + "skin", + "cooperative", + "drops", + ].forEach(function (p) { + //same array as game.js adopt_soft_properties + if (p in monster && monster[p] != def[p]) { + data[p] = monster[p]; + } }); - if(monster.max_hp!=def.hp) data.max_hp=monster.max_hp; - ["id","x","y","moving","going_x","going_y","abs","move_num","angle","type","cid","target","focus","s"].forEach(function(p){ // removed "vx","vy" from both datasets [16/04/18] - if(monster[p]!==undefined) data[p]=monster[p]; + if (monster.max_hp != def.hp) { + data.max_hp = monster.max_hp; + } + [ + "id", + "x", + "y", + "moving", + "going_x", + "going_y", + "abs", + "move_num", + "angle", + "type", + "cid", + "target", + "focus", + "s", + ].forEach(function (p) { + // removed "vx","vy" from both datasets [16/04/18] + if (monster[p] !== undefined) { + data[p] = monster[p]; + } }); - if(monster.level>1) data.level=monster.level; - if(monster.pet) data.pet=true,data.owner=monster.owner,data.name=monster.name; - if(monster.trap) data.trap=true,data.owner=monster.owner; + if (monster.level > 1) { + data.level = monster.level; + } + if (monster.pet) { + data.pet = true; + data.owner = monster.owner; + data.name = monster.name; + } + if (monster.trap) { + data.trap = true; + data.owner = monster.owner; + } - if(events && events.length) data.events=events; + if (events && events.length) { + data.events = events; + } return data; } -function player_to_summary(player) -{ - var summary={skin:player.tskin||player.skin,level:player.level,type:player.type,x:player.x,y:player.y,in:player.in,map:player.map,name:player.name,hp:player.hp,max_hp:player.max_hp}; - if(player.rip) summary.rip=(player.tcx&&player.tcx.gravestone||player.cx.gravestone)||true; - summary.cx=(player.tcx||player.cx||{}); +function player_to_summary(player) { + var summary = { + skin: player.tskin || player.skin, + level: player.level, + type: player.type, + x: player.x, + y: player.y, + in: player.in, + map: player.map, + name: player.name, + hp: player.hp, + max_hp: player.max_hp, + }; + if (player.rip) { + summary.rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone || true; + } + summary.cx = player.tcx || player.cx || {}; return summary; } -function save_state(player) -{ - player.state={"map":player.map,"in":player.in,"hp":player.hp,"mp":player.mp,"s":player.s,"x":player.x,"y":player.y,"restored":false}; +function save_state(player) { + player.state = { + map: player.map, + in: player.in, + hp: player.hp, + mp: player.mp, + s: player.s, + x: player.x, + y: player.y, + restored: false, + }; } -function clean_slate(player) -{ +function clean_slate(player) { restore_state(player); save_state(player); - player.hp=player.max_hp; - player.mp=player.max_mp; - player.s={}; - player.c={}; + player.hp = player.max_hp; + player.mp = player.max_mp; + player.s = {}; + player.c = {}; } -function restore_state(player,dc) -{ - if(player.state && !player.state.restored) - { - player.hp=player.state.hp; - player.mp=player.state.mp; - player.s=player.state.s; - player.rip=false; if(player.state.rip) player.rip=true; - if(dc) - { - player.map=player.state.map; - player.in=player.state.in; - player.x=player.state.x; - player.y=player.state.y; +function restore_state(player, dc) { + if (player.state && !player.state.restored) { + player.hp = player.state.hp; + player.mp = player.state.mp; + player.s = player.state.s; + player.rip = false; + if (player.state.rip) { + player.rip = true; } - player.state.restored=true; + if (dc) { + player.map = player.state.map; + player.in = player.state.in; + player.x = player.state.x; + player.y = player.state.y; + } + player.state.restored = true; } delete player.team; delete player.duel; } -function party_to_client(oname) -{ - var list=parties[oname]; - var party={},output=0,length=0,newbies=0,odps={},c={},add=36000; +function party_to_client(oname) { + var list = parties[oname]; + var party = {}; + var output = 0; + var length = 0; + var newbies = 0; + var odps = {}; + var c = {}; + var add = 36000; calculate_party(oname); - list.forEach(function(name){ - var player=players[name_to_id[name]],dps_multiplier=1; if(!player) return; - if(player.type=="merchant") return; - if(player.type=="priest") dps_multiplier=1.36; - length+=1; - if(player.level<60) newbies+=1; - output+=player.pdps*dps_multiplier+add; - odps[player.owner]=(odps[player.owner]||0)+player.pdps*dps_multiplier+add; - c[player.owner]=(c[player.owner]||0)+1; + list.forEach(function (name) { + var player = players[name_to_id[name]]; + var dps_multiplier = 1; + if (!player) { + return; + } + if (player.type == "merchant") { + return; + } + if (player.type == "priest") { + dps_multiplier = 1.36; + } + length += 1; + if (player.level < 60) { + newbies += 1; + } + output += player.pdps * dps_multiplier + add; + odps[player.owner] = (odps[player.owner] || 0) + player.pdps * dps_multiplier + add; + c[player.owner] = (c[player.owner] || 0) + 1; }); - list.forEach(function(name){ - var player=players[name_to_id[name]],dps_multiplier=1; if(!player) return; - if(player.type=="priest") dps_multiplier=1.36; - player.share=0; - if(!output) // to handle an all merchant party - { - player.share=1.0/(max(1,list.length)+EPS); + list.forEach(function (name) { + var player = players[name_to_id[name]]; + var dps_multiplier = 1; + if (!player) { + return; } - else if(player.type!="merchant") - { - player.share=odps[player.owner]/(c[player.owner]+EPS)/(output+EPS); - player.share=min(1,player.share); + if (player.type == "priest") { + dps_multiplier = 1.36; + } + player.share = 0; + if (!output) { + // to handle an all merchant party + player.share = 1.0 / (max(1, list.length) + EPS); + } else if (player.type != "merchant") { + player.share = odps[player.owner] / (c[player.owner] + EPS) / (output + EPS); + player.share = min(1, player.share); + } + player.party_length = length; + player.party_gold = 5; + player.party_luck = newbies * 10; + player.party_xp = [0, 0, 10, 16, 20, 24, 25, 30, 36, 40, 40, 40, 40][length] || 0; + party[name] = { + skin: player.tskin || player.skin, + level: player.level, + type: player.type, + x: player.x, + y: player.y, + in: player.in, + map: player.map, + share: player.share, + pdps: player.pdps, + l: length, + xp: player.party_xp, + luck: player.party_luck, + gold: player.party_gold, + }; + if (player.rip) { + party[name].rip = (player.tcx && player.tcx.gravestone) || player.cx.gravestone || true; + } + if (Object.keys(player.tcx || player.cx).length) { + party[name].cx = player.tcx || player.cx; } - player.party_length=length; - player.party_gold=5; - player.party_luck=newbies*10; - player.party_xp=[0,0,10,16,20,24,25,30,36,40,40,40,40][length]||0; - party[name]={skin:player.tskin||player.skin,level:player.level,type:player.type,x:player.x,y:player.y,in:player.in,map:player.map,share:player.share,pdps:player.pdps,l:length,xp:player.party_xp,luck:player.party_luck,gold:player.party_gold}; - if(player.rip) party[name].rip=(player.tcx&&player.tcx.gravestone||player.cx.gravestone)||true; - if(Object.keys(player.tcx||player.cx).length) party[name].cx=(player.tcx||player.cx); }); // git test return party; } -function send_party_update(oname) -{ - var party=party_to_client(oname); - parties[oname].forEach(function(name){ - var player=players[name_to_id[name]]; - if(!player) { console.log("#X party player not found: "+oname); leave_party(oname,{name:name}); return; } - players[name_to_id[name]].socket.emit("party_update",{list:parties[oname],party:party}); +function send_party_update(oname) { + var party = party_to_client(oname); + parties[oname].forEach(function (name) { + var player = players[name_to_id[name]]; + if (!player) { + console.log("#X party player not found: " + oname); + leave_party(oname, { name: name }); + return; + } + players[name_to_id[name]].socket.emit("party_update", { list: parties[oname], party: party }); }); } -function calculate_party(oname) -{ - var list=parties[oname]; - list.forEach(function(name){ - var player=players[name_to_id[name]]; if(!player) return; - if(player.type=="merchant") player.party_weight=0; - player.party_weight=20; +function calculate_party(oname) { + var list = parties[oname]; + list.forEach(function (name) { + var player = players[name_to_id[name]]; + if (!player) { + return; + } + if (player.type == "merchant") { + player.party_weight = 0; + } + player.party_weight = 20; }); } -var stat_to_attr={ - "str":"str", - "int":"int", - "dex":"dex", - "vit":"vit", - "for":"for", - "armor":"armor", - "resistance":"resistance", - "pnresistance":"pnresistance", - "firesistance":"firesistance", - "fzresistance":"fzresistance", - "phresistance":"phresistance", - "stresistance":"stresistance", - "incdmgamp":"incdmgamp", - "stun":"stun", - "blast":"blast", - "explosion":"explosion", - "evasion":"evasion", - "cuteness":"cuteness", - "bling":"bling", - "dreturn":"dreturn", - "reflection":"reflection", - "crit":"crit", - "critdamage":"critdamage", - "miss":"miss", - "avoidance":"avoidance", - "hp":"max_hp", - "mp":"max_mp", - "speed":"speed", - "lifesteal":"lifesteal", - "manasteal":"manasteal", - "apiercing":"apiercing", - "rpiercing":"rpiercing", - "output":"output", - "attack":"a_attack", - "mp_cost":"a_mp_cost", - "mp_reduction":"mp_reduction", - "xp":"xxp", - "luck":"xluck", - "gold":"xgold", - "range":"range", - "courage":"courage", - "mcourage":"mcourage", - "pcourage":"pcourage", -} - -function apply_stats(player,prop,args) -{ - for(var stat in prop) - { - if(args && args.no_range && stat=="range") continue; - if(stat_to_attr[stat]) - player[stat_to_attr[stat]]=player[stat_to_attr[stat]]+prop[stat]; - else if(stat=="frequency") player.frequency+=prop[stat]/100.0; +var stat_to_attr = { + str: "str", + int: "int", + dex: "dex", + vit: "vit", + for: "for", + armor: "armor", + resistance: "resistance", + pnresistance: "pnresistance", + firesistance: "firesistance", + fzresistance: "fzresistance", + phresistance: "phresistance", + stresistance: "stresistance", + incdmgamp: "incdmgamp", + stun: "stun", + blast: "blast", + explosion: "explosion", + evasion: "evasion", + cuteness: "cuteness", + bling: "bling", + dreturn: "dreturn", + reflection: "reflection", + crit: "crit", + critdamage: "critdamage", + miss: "miss", + avoidance: "avoidance", + hp: "max_hp", + mp: "max_mp", + speed: "speed", + lifesteal: "lifesteal", + manasteal: "manasteal", + apiercing: "apiercing", + rpiercing: "rpiercing", + output: "output", + attack: "a_attack", + mp_cost: "a_mp_cost", + mp_reduction: "mp_reduction", + xp: "xxp", + luck: "xluck", + gold: "xgold", + range: "range", + courage: "courage", + mcourage: "mcourage", + pcourage: "pcourage", +}; +function apply_stats(player, prop, args) { + for (var stat in prop) { + if (args && args.no_range && stat == "range") { + continue; + } + if (stat_to_attr[stat]) { + player[stat_to_attr[stat]] = player[stat_to_attr[stat]] + prop[stat]; + } else if (stat == "frequency") { + player.frequency += prop[stat] / 100.0; + } } } -function calculate_player_stats(player) -{ - if(player.is_npc) return; - player.max_xp=G.levels[player.level+""]; - var level_up=false; - while(player.xp>=player.max_xp) - { - level_up=true; - player.xp-=player.max_xp; +function calculate_player_stats(player) { + if (player.is_npc) { + return; + } + player.max_xp = G.levels[player.level + ""]; + var level_up = false; + while (player.xp >= player.max_xp) { + level_up = true; + player.xp -= player.max_xp; player.level++; - player.max_xp=G.levels[player.level+""]; - player.hp=0; + player.max_xp = G.levels[player.level + ""]; + player.hp = 0; achievement_logic_level(player); } - if(level_up) - { - if(player.level>=80) realm_broadcast("server_message",{message:player.name+" is now level "+player.level,color:"#968CFA"}); - else if(player.level>=70) broadcast("server_message",{message:player.name+" is now level "+player.level+"!",color:"#968CFA"}); - xy_emit(player,"ui",{type:"level_up",name:player.name}); + if (level_up) { + if (player.level >= 80) { + realm_broadcast("server_message", { message: player.name + " is now level " + player.level, color: "#968CFA" }); + } else if (player.level >= 70) { + broadcast("server_message", { message: player.name + " is now level " + player.level + "!", color: "#968CFA" }); + } + xy_emit(player, "ui", { type: "level_up", name: player.name }); } // disappearing_text(player.socket,player,"LEVEL UP!",{xy:1,size:"huge",color:"#724A8F"}); - if(player.xp<0) - { - player.xp=0; - player.warnings=(player.warnings||0)+1; - if(player.warnings==2) - { + if (player.xp < 0) { + player.xp = 0; + player.warnings = (player.warnings || 0) + 1; + if (player.warnings == 2) { console.log("'Your monster!' logged out ->"); - player.socket.emit("ui_log","You monster!"); + player.socket.emit("ui_log", "You monster!"); player.socket.disconnect(); return; } } - var class_def=G.classes[player.type],item_attack=0,the_date=new Date(); - if(!class_def) class_def=G.classes.merchant; // when npc's get resend't - player.range=class_def.range; - player.max_hp=class_def.hp; - player.max_mp=class_def.mp; - ["stealth"].forEach(function(prop){player[prop]=false;}); - ["a_mp_cost","a_attack","stones","lifesteal","manasteal","incdmgamp","mp_reduction","stun","blast","explosion","cuteness","bling"].forEach(function(prop){player[prop]=0;}); - ["speed","attack","frequency","mp_cost","armor","resistance","apiercing","rpiercing","a","aura","evasion","miss","reflection","crit","critdamage","dreturn","computer","xxp","xluck","xgold","output","courage","mcourage","pcourage","pnresistance","firesistance","fzresistance","phresistance","stresistance"].forEach(function(prop){ - player[prop]=class_def[prop]||0; + var class_def = G.classes[player.type]; + var item_attack = 0; + var the_date = new Date(); + if (!class_def) { + class_def = G.classes.merchant; + } // when npc's get resend't + player.range = class_def.range; + player.max_hp = class_def.hp; + player.max_mp = class_def.mp; + ["stealth"].forEach(function (prop) { + player[prop] = false; }); - if(!player.a) player.a={}; //abilities - if(!player.aura) player.aura={}; - if(player.paura) for(var id in player.paura) player.aura[id]=player.paura[id]; - for(stat in class_def.stats) - { - player[stat]=class_def.stats[stat]+player.level*class_def.lstats[stat]; - if(player.level>40) player[stat]+=(player.level-40)*class_def.lstats[stat]; - if(player.level>55) player[stat]+=(player.level-55)*class_def.lstats[stat]; - if(player.level>65) player[stat]+=(player.level-65)*class_def.lstats[stat]; - if(player.level>80) player[stat]-=(player.level-80)*class_def.lstats[stat]; - player[stat]=floor(player[stat]); + [ + "a_mp_cost", + "a_attack", + "stones", + "lifesteal", + "manasteal", + "incdmgamp", + "mp_reduction", + "stun", + "blast", + "explosion", + "cuteness", + "bling", + ].forEach(function (prop) { + player[prop] = 0; + }); + [ + "speed", + "attack", + "frequency", + "mp_cost", + "armor", + "resistance", + "apiercing", + "rpiercing", + "a", + "aura", + "evasion", + "miss", + "reflection", + "crit", + "critdamage", + "dreturn", + "computer", + "xxp", + "xluck", + "xgold", + "output", + "courage", + "mcourage", + "pcourage", + "pnresistance", + "firesistance", + "fzresistance", + "phresistance", + "stresistance", + ].forEach(function (prop) { + player[prop] = class_def[prop] || 0; + }); + if (!player.a) { + player.a = {}; + } //abilities + if (!player.aura) { + player.aura = {}; + } + if (player.paura) { + for (var id in player.paura) { + player.aura[id] = player.paura[id]; + } + } + for (stat in class_def.stats) { + player[stat] = class_def.stats[stat] + player.level * class_def.lstats[stat]; + if (player.level > 40) { + player[stat] += (player.level - 40) * class_def.lstats[stat]; + } + if (player.level > 55) { + player[stat] += (player.level - 55) * class_def.lstats[stat]; + } + if (player.level > 65) { + player[stat] += (player.level - 65) * class_def.lstats[stat]; + } + if (player.level > 80) { + player[stat] -= (player.level - 80) * class_def.lstats[stat]; + } + player[stat] = floor(player[stat]); + } + if (!player.slots) { + player.slots = {}; } - if(!player.slots) player.slots={}; // players.citems[27]=null; //- to reproduce the bug - while(player.items.length>42 && !player.items[player.items.length-1]) player.items.splice(player.items.length-1); - while(player.citems.length>42 && !player.citems[player.citems.length-1]) player.citems.splice(player.citems.length-1); - player.isize=42; player.sets={}; - player.esize=player.isize-player.items.length; - player.xpm=player.goldm=player.luckm=1; - for(var i=0;icurrent.expires) - { - player.items[i]=null; player.citems[i]=null; - } - else if(G.items[current.name].gain) - { - var prop=calculate_item_properties(current); + while (player.items.length > 42 && !player.items[player.items.length - 1]) { + player.items.splice(player.items.length - 1); + } + while (player.citems.length > 42 && !player.citems[player.citems.length - 1]) { + player.citems.splice(player.citems.length - 1); + } + player.isize = 42; + player.sets = {}; + player.esize = player.isize - player.items.length; + player.xpm = player.goldm = player.luckm = 1; + for (var i = 0; i < player.items.length; i++) { + var current = player.items[i]; + if (!current) { + player.esize++; + } else if (current.name == "supercomputer") { + player.tracker = player.computer = player.supercomputer = true; + } else if (current.name == "computer") { + player.computer = true; + } else if (current.name == "tracker") { + player.tracker = true; + } else if (current.expires) { + // &&!current.ex = ex=elixier + if (the_date > current.expires) { + player.items[i] = null; + player.citems[i] = null; + } else if (G.items[current.name].gain) { + var prop = calculate_item_properties(current); player.stones++; - player["x"+G.items[current.name].gain]=prop[G.items[current.name].gain]; + player["x" + G.items[current.name].gain] = prop[G.items[current.name].gain]; } } } - player.monster_stats={}; - if(player.tracker) - { - for(var name in G.monsters) - { - var mx=max((player.p.stats.monsters[name]||0)+(player.p.stats.monsters_diff[name]||0),(player.max_stats.monsters[name]||[0,0])[0]); - if(!mx || !G.monsters[name].achievements) continue; - G.monsters[name].achievements.forEach(function(def){ - if(mx 80 && 0.01) || + (player.level > 80 && 0.012) || + (player.level > 70 && 0.02) || + (player.level > 60 && 0.025) || + (player.level > 50 && 0.03) || + (player.level > 20 && 0.04) || + 0.05; - if(player.s.invis) player.speed=max(player.speed*0.6,player.speed-25),player.attack=round(player.attack*1.25); - if(player.s.invincible) player.attack=round(player.attack*0.45); - if(player.s.dash) player.speed=500; - calculate_common_stats(player) + var excess = max( + 0, + max(player.targets_p - player.courage, max(player.targets_m - player.mcourage, player.targets_u - player.pcourage)), + ); + var sredux = [0, 20, 40, 70, 80, 90, 100]; + player.speed -= sredux[min(sredux.length - 1, excess)]; + if (excess > 2) { + player.attack = round(player.attack * 0.2); + } else if (excess > 1) { + player.attack = round(player.attack * 0.4); + } else if (excess) { + player.attack = round(player.attack * 0.6); + } + player.fear = excess; - player.tax=(player.level>80)&&0.010||(player.level>80)&&0.012||(player.level>70)&&0.020||(player.level>60)&&0.025||(player.level>50)&&0.03||(player.level>20)&&0.04||0.05; + if (player.map == "winterland") { + player.speed *= 0.95; + } - var excess=max(0,max(player.targets_p-player.courage,max(player.targets_m-player.mcourage,player.targets_u-player.pcourage))); - var sredux=[0,20,40,70,80,90,100]; - player.speed-=sredux[min(sredux.length-1,excess)]; - if(excess>2) player.attack=round(player.attack*0.2); - else if(excess>1) player.attack=round(player.attack*0.4); - else if(excess) player.attack=round(player.attack*0.6); - player.fear=excess; - - if(player.map=="winterland") player.speed*=0.95; - - if(player.p.stand || player.s.hardshell) player.speed=10; - player.evasion=min(50,player.evasion); - player.reflection=min(player.s.reflection&&50||30,player.reflection); - player.speed=min(player.speed,player.cruise||200000); - player.speed=round(max(5,player.speed)); - if(!player.gold && player.gold!==0) player.gold=0,server_log("#X - GOLD BUG calculate",1); + if (player.p.stand || player.s.hardshell) { + player.speed = 10; + } + player.evasion = min(50, player.evasion); + player.reflection = min((player.s.reflection && 50) || 30, player.reflection); + player.speed = min(player.speed, player.cruise || 200000); + player.speed = round(max(5, player.speed)); + if (!player.gold && player.gold !== 0) { + player.gold = 0; + server_log("#X - GOLD BUG calculate", 1); + } recalculate_vxy(player); - perfc.cps+=1; + perfc.cps += 1; } -function calculate_common_stats(entity) -{ - if(entity.s.poisoned) entity.frequency*=0.9; - if(entity.s.frozen) entity.frequency*=0.3,entity.speed-=40; - if(entity.speed<1) entity.speed=1; - if(entity.s.tangled) entity.speed=min(entity.speed,24); +function calculate_common_stats(entity) { + if (entity.s.poisoned) { + entity.frequency *= 0.9; + } + if (entity.s.frozen) { + entity.frequency *= 0.3; + entity.speed -= 40; + } + if (entity.speed < 1) { + entity.speed = 1; + } + if (entity.s.tangled) { + entity.speed = min(entity.speed, 24); + } } - -function calculate_monster_stats(monster) -{ - var def=G.monsters[monster.type]; - ["attack","speed","frequency","armor","resistance","output","incdmgamp","avoidance"].forEach(function(p){ - monster[p]=def[p]||0; +function calculate_monster_stats(monster) { + var def = G.monsters[monster.type]; + ["attack", "speed", "frequency", "armor", "resistance", "output", "incdmgamp", "avoidance"].forEach(function (p) { + monster[p] = def[p] || 0; }); - monster.output=100; - if(monster.target || monster.focus) monster.speed=G.monsters[monster.type].charge; - for(var name in monster.s) - { + monster.output = 100; + if (monster.target || monster.focus) { + monster.speed = G.monsters[monster.type].charge; + } + for (var name in monster.s) { // if(G.conditions[name]) // { // for(var p in G.conditions[name]) @@ -982,6679 +1484,10051 @@ function calculate_monster_stats(monster) // monster[p]+=G.conditions[name][p]; // } - var prop=G.conditions[name]; - apply_stats(monster,monster.s[name]) - if(!prop) continue; - apply_stats(monster,prop); + var prop = G.conditions[name]; + apply_stats(monster, monster.s[name]); + if (!prop) { + continue; + } + apply_stats(monster, prop); } - monster.attack=round(monster.attack*monster.output/100.0) - if(monster.focus && instances[monster.in].monsters[monster.focus]) monster.speed=min(monster.speed,instances[monster.in].monsters[monster.focus].speed+4); - if(monster.level>1) - { - var att_mult=0.125,freq_mult=0.034,speed_mult=0.24; // speed originally 0.34 - var mlevel=min(monster.level,12); - if(monster.map_def.grow) att_mult=0.05,freq_mult=0.008,speed_mult=0.16; - monster.attack=parseInt(monster.attack*(1+mlevel*att_mult)); - monster.speed+=mlevel*speed_mult; - monster.frequency+=mlevel*freq_mult; + monster.attack = round((monster.attack * monster.output) / 100.0); + if (monster.focus && instances[monster.in].monsters[monster.focus]) { + monster.speed = min(monster.speed, instances[monster.in].monsters[monster.focus].speed + 4); + } + if (monster.level > 1) { + var att_mult = 0.125; + var freq_mult = 0.034; + var speed_mult = 0.24; // speed originally 0.34 + var mlevel = min(monster.level, 12); + if (monster.map_def.grow) { + att_mult = 0.05; + freq_mult = 0.008; + speed_mult = 0.16; + } + monster.attack = parseInt(monster.attack * (1 + mlevel * att_mult)); + monster.speed += mlevel * speed_mult; + monster.frequency += mlevel * freq_mult; + } + if (E.schedule.night) { + monster.speed = ceil(monster.speed * 0.7); } - if(E.schedule.night) monster.speed=ceil(monster.speed*0.7); calculate_common_stats(monster); recalculate_vxy(monster); } -function ccms(monster) -{ - if(Object.keys(monster.s).length) calculate_monster_stats(monster); +function ccms(monster) { + if (Object.keys(monster.s).length) { + calculate_monster_stats(monster); + } } -function can_equip_item(player,item,slot) -{ - var class_def=G.classes[player.type]; - if(!slot) slot=item.type; - if(slot=="offhand" && item.type!="weapon") slot=item.type; //Easiest solution to the slot:"offhand" challenge [15/11/16] - if(item.type=="tool") slot="mainhand"; - if(item.type=="test" && slot!="test") return slot; - if(!in_arr(item.type,["helmet","pants","chest","weapon","amulet","earring","shoes","gloves","ring","shield","belt","source","orb","quiver","cape","misc_offhand","tool"])) return "no"; - if(slot!=item.type && in_arr(item.type,["shield","source","quiver","misc_offhand"]) && slot!="offhand") return "no"; - if(in_arr(slot,["weapon","mainhand","offhand","tool"])) - { - if(slot=="weapon" && player.slots.mainhand && class_def.mainhand[G.items[player.slots.mainhand.name].wtype] && !player.slots.offhand && class_def.offhand[item.wtype]) - { - slot="offhand"; +function can_equip_item(player, item, slot) { + var class_def = G.classes[player.type]; + if (!slot) { + slot = item.type; + } + if (slot == "offhand" && item.type != "weapon") { + slot = item.type; + } //Easiest solution to the slot:"offhand" challenge [15/11/16] + if (item.type == "tool") { + slot = "mainhand"; + } + if (item.type == "test" && slot != "test") { + return slot; + } + if ( + !in_arr(item.type, [ + "helmet", + "pants", + "chest", + "weapon", + "amulet", + "earring", + "shoes", + "gloves", + "ring", + "shield", + "belt", + "source", + "orb", + "quiver", + "cape", + "misc_offhand", + "tool", + ]) + ) { + return "no"; + } + if (slot != item.type && in_arr(item.type, ["shield", "source", "quiver", "misc_offhand"]) && slot != "offhand") { + return "no"; + } + if (in_arr(slot, ["weapon", "mainhand", "offhand", "tool"])) { + if ( + slot == "weapon" && + player.slots.mainhand && + class_def.mainhand[G.items[player.slots.mainhand.name].wtype] && + !player.slots.offhand && + class_def.offhand[item.wtype] + ) { + slot = "offhand"; + } else if ( + slot == "offhand" && + (!player.slots.mainhand || + (player.slots.mainhand && class_def.mainhand[G.items[player.slots.mainhand.name].wtype])) && + class_def.offhand[item.wtype] + ) { + slot = "offhand"; + } else if (slot == "weapon" || slot == "mainhand") { + if (class_def.doublehand[item.wtype] && !player.slots.offhand) { + } else if (!class_def.mainhand[item.wtype]) { + return "no"; + } + slot = "mainhand"; + } else { + return "no"; } - else if(slot=="offhand" && (!player.slots.mainhand || player.slots.mainhand && class_def.mainhand[G.items[player.slots.mainhand.name].wtype]) && class_def.offhand[item.wtype]) - { - slot="offhand"; + } else if (item.type == "shield" || item.type == "misc_offhand") { + if ( + class_def.offhand[item.type] && + !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) + ) { + slot = "offhand"; + } else { + return "no"; } - else if(slot=="weapon" || slot=="mainhand") - { - if(class_def.doublehand[item.wtype] && !player.slots.offhand); - else if(!class_def.mainhand[item.wtype]) return "no"; - slot="mainhand"; + } else if (item.type == "quiver") { + if ( + class_def.offhand.quiver && + !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) + ) { + slot = "offhand"; + } else { + return "no"; } - else {return "no";} - } - else if(item.type=="shield" || item.type=="misc_offhand") - { - if(class_def.offhand[item.type] && !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype])) slot="offhand"; - else {return "no";} - } - else if(item.type=="quiver") - { - if(class_def.offhand.quiver && !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype])) slot="offhand"; - else {return "no";} - } - else if(item.type=="source") - { - if(class_def.offhand.source && !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype])) slot="offhand"; - else {return "no";} - } - else if(item.type=="ring") - { - if(slot=="ring1" || slot=="ring2"); - else if(!player.slots["ring1"]) slot="ring1"; - else slot="ring2"; - } - else if(item.type=="earring") - { - if(slot=="earring1" || slot=="earring2"); - else if(!player.slots["earring1"]) slot="earring1"; - else slot="earring2"; - } - else if(slot!=item.type) - { + } else if (item.type == "source") { + if ( + class_def.offhand.source && + !(player.slots.mainhand && class_def.doublehand[G.items[player.slots.mainhand.name].wtype]) + ) { + slot = "offhand"; + } else { + return "no"; + } + } else if (item.type == "ring") { + if (slot == "ring1" || slot == "ring2") { + } else if (!player.slots["ring1"]) { + slot = "ring1"; + } else { + slot = "ring2"; + } + } else if (item.type == "earring") { + if (slot == "earring1" || slot == "earring2") { + } else if (!player.slots["earring1"]) { + slot = "earring1"; + } else { + slot = "earring2"; + } + } else if (slot != item.type) { return "no"; } return slot; } -function consume(player,num,quantity) -{ - var available=player.items[num].q||1; - if(quantity>available) exception=not_enough_items; - if(available==quantity) player.items[num]=null,player.esize++; - else player.items[num].q-=quantity; - player.citems[num]=cache_item(player.items[num]); +function consume(player, num, quantity) { + var available = player.items[num].q || 1; + if (quantity > available) { + exception = not_enough_items; + } + if (available == quantity) { + player.items[num] = null; + player.esize++; + } else { + player.items[num].q -= quantity; + } + player.citems[num] = cache_item(player.items[num]); } -function consume_one(player,num) -{ - consume(player,num,1); +function consume_one(player, num) { + consume(player, num, 1); } -function consume_one_by_id(player,id) -{ - for(var i=0;i0.00001) && Math.random()/share/player.luckm/monster.level/monster_mult0.00001) && Math.random()/share/player.luckm/monster.level/monster_mult 0.00001) && + Math.random() / share / player.luckm / monster.level / monster_mult < item[0]) || + mode.drop_all + ) { + // /hp_mult - removed [13/07/18] + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (monster.drops) { + monster.drops.forEach(function (item) { + if ( + ((!monster.temp || item[0] > 0.00001) && + Math.random() / share / player.luckm / monster.level / monster_mult < item[0]) || + mode.drop_all + ) { + // /hp_mult - removed [13/07/18] + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (player.tskin == "konami") { + D.drops.konami.forEach(function (item) { + if (Math.random() / share / player.luckm / monster.level < item[0] || mode.drop_all) { + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (player.p.first && !player.p.first_drop) { + player.p.first_drop = true; + drop.gold += 100000; drop.items.push(create_new_item("ringsj")); drop.items.push(create_new_item("ringsj")); drop.items.push(create_new_item("ringsj")); drop.items.push(create_new_item("hpbelt")); drop.items.push(create_new_item("gem0")); } - if(Math.random()1) // && !m.target - it was possible to keep a high level monsters aggroed and abuse the system [21/07/23] - { - player.s.monsterhunt.dl=true; - level_monster(m,{delevel:true}); +function monster_hunt_logic(player, monster) { + var target = monster; + if (!player.s.monsterhunt || player.s.monsterhunt.sn != region + " " + server_name) { + return; + } + if (player.s.monsterhunt.id == monster.type && player.s.monsterhunt.c) { + player.s.monsterhunt.c--; + } + if (target.level == 1 && player.s.monsterhunt.id == target.type && player.s.monsterhunt.dl) { + player.s.monsterhunt.dl = false; + get_monsters(player.s.monsterhunt.id).forEach(function (m) { + if (!player.s.monsterhunt.dl && m.level > 1) { + // && !m.target - it was possible to keep a high level monsters aggroed and abuse the system [21/07/23] + player.s.monsterhunt.dl = true; + level_monster(m, { delevel: true }); } }); } } -function calculate_monster_score(player,monster,share) -{ - var score=min(1,monster.mult*2.2); - var divider=1; - if(!share) share=0; - if(monster.cooperative) divider=2; - for(var id in players) - { - var current=players[id]; - if(current.id==player.id) continue; - if(current.owner==player.owner && current.type=="merchant" && simple_distance(current,player)<600) score-=0.2/divider; - if(current.party && current.party==player.party && current.type=="merchant" && simple_distance(current,player)<600) score-=0.1/divider; - else if(current.owner==player.owner && current.party==player.party && simple_distance(current,player)<600 && current.type!="merchant") score+=0.3/divider; - else if(current.owner==player.owner && current.party && current.party==player.party && current.type!="merchant") score+=0.3/divider; // originally 0.25 +function calculate_monster_score(player, monster, share) { + var score = min(1, monster.mult * 2.2); + var divider = 1; + if (!share) { + share = 0; } - if(simple_distance(player,monster)>600 && share<0.01) - { - score-=0.3/divider; + if (monster.cooperative) { + divider = 2; + } + for (var id in players) { + var current = players[id]; + if (current.id == player.id) { + continue; + } + if (current.owner == player.owner && current.type == "merchant" && simple_distance(current, player) < 600) { + score -= 0.2 / divider; + } + if ( + current.party && + current.party == player.party && + current.type == "merchant" && + simple_distance(current, player) < 600 + ) { + score -= 0.1 / divider; + } else if ( + current.owner == player.owner && + current.party == player.party && + simple_distance(current, player) < 600 && + current.type != "merchant" + ) { + score += 0.3 / divider; + } else if ( + current.owner == player.owner && + current.party && + current.party == player.party && + current.type != "merchant" + ) { + score += 0.3 / divider; + } // originally 0.25 + } + if (simple_distance(player, monster) > 600 && share < 0.01) { + score -= 0.3 / divider; + } + if (player.type == "merchant" && player.party) { + score /= 2; + } + if (score < 0) { + score = 0; + } + if (gameplay == "hardcore") { + score *= 10000; } - if(player.type=="merchant" && player.party) score/=2; - if(score<0) score=0; - if(gameplay=="hardcore") score*=10000; return score; } -function issue_monster_awards(monster) -{ - var total=0.1; - for(var name in monster.points) - { - var current=players[name_to_id[name]]; - if(current) // && current.map==monster.map - total+=max(0,monster.points[name]); +function issue_monster_awards(monster) { + var total = 0.1; + for (var name in monster.points) { + var current = players[name_to_id[name]]; + if (current) { + // && current.map==monster.map + total += max(0, monster.points[name]); + } } - for(var name in monster.points) - { - var current=players[name_to_id[name]]; - var share=max(0,monster.points[name])/total; - if(current && share>0.0025) // && current.map==monster.map - { - if(monster.rbuff && G.conditions[monster.rbuff]) current.s[monster.rbuff]={ms:G.conditions[monster.rbuff].duration}; - if(monster.cbuff) for(var i=0;i 0.0025) { + // && current.map==monster.map + if (monster.rbuff && G.conditions[monster.rbuff]) { + current.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; + } + if (monster.cbuff) { + for (var i = 0; i < monster.cbuff.length; i++) { + if (current.level <= monster.cbuff[i][0] && G.conditions[monster.cbuff[i][1]]) { + current.s[monster.cbuff[i][1]] = { ms: G.conditions[monster.cbuff[i][1]].duration }; + break; + } + } + } + if (G.monsters[monster.type]["1hp"]) { + drop_something(current, monster); + } else { + drop_something(current, monster, share); + } + var score = calculate_monster_score(current, monster, share); + current.p.stats.monsters[monster.type] = (current.p.stats.monsters[monster.type] || 0) + 1; + current.p.stats.monsters_diff[monster.type] = (current.p.stats.monsters_diff[monster.type] || 0) + (score - 1); + monster_hunt_logic(current, monster, share); + if (current.type == "merchant") { + continue; + } + current.xp += round(monster.xp * share * current.xpm); + if (current.t) { + current.t.xp += round(monster.xp * share * current.xpm); + } delete current.s.coop; - resend(current,"u+cid"); + resend(current, "u+cid"); } } } -function issue_monster_award(monster) -{ - if(monster.cooperative) return issue_monster_awards(monster); - var player=players[name_to_id[monster.target]]; - if(!player) return; +function issue_monster_award(monster) { + if (monster.cooperative) { + return issue_monster_awards(monster); + } + var player = players[name_to_id[monster.target]]; + if (!player) { + return; + } // if(gameplay=="test" && player.level<80) player.level+=1; stats.kills[monster.type]++; - drop_something(player,monster); - if(!player.party) - { - if(monster.rbuff && G.conditions[monster.rbuff]) player.s[monster.rbuff]={ms:G.conditions[monster.rbuff].duration}; - if(monster.cbuff) for(var i=0;i=10) lost_gold=1000; - if(target.level>=20) lost_gold=5000; - if(target.level>=30) lost_gold=12500; - if(target.level>=40) lost_gold=25000; - if(target.level>=50) lost_gold=50000; - if(target.level>=55) lost_gold=75000; - if(target.level>=60) lost_gold=125000; - if(target.level>=65) lost_gold=250000; - if(target.level>=70) lost_gold=500000; - if(target.level>=75) lost_gold=1000000; - - lost_gold=min(lost_gold, max(attacker.gold,10000)*4 )||0; - lost_xp=round(min(lost_xp, max(attacker.xp/15.0,50000) ))||0; - - if(G.maps[attacker.map].safe_pvp && !is_pvp) - { - lost_gold=0; - lost_xp=0; + var lost_gold = 100; + var gain_gold = 100; + if (target.level >= 10) { + lost_gold = 1000; + } + if (target.level >= 20) { + lost_gold = 5000; + } + if (target.level >= 30) { + lost_gold = 12500; + } + if (target.level >= 40) { + lost_gold = 25000; + } + if (target.level >= 50) { + lost_gold = 50000; + } + if (target.level >= 55) { + lost_gold = 75000; + } + if (target.level >= 60) { + lost_gold = 125000; + } + if (target.level >= 65) { + lost_gold = 250000; + } + if (target.level >= 70) { + lost_gold = 500000; + } + if (target.level >= 75) { + lost_gold = 1000000; } + lost_gold = min(lost_gold, max(attacker.gold, 10000) * 4) || 0; + lost_xp = round(min(lost_xp, max(attacker.xp / 15.0, 50000))) || 0; - if(gameplay=="hardcore") - { - lost_gold=round(target.gold*0.8); - lost_xp=target.xp; - for(var i=1;i<=B.hlevel_loss;i++) - if(target.level-i>0) - lost_xp+=G.levels[(target.level-i)+""]; - target.level=max(1,target.level-B.hlevel_loss); + if (G.maps[attacker.map].safe_pvp && !is_pvp) { + lost_gold = 0; + lost_xp = 0; } - - lost_gold=min(target.gold,lost_gold); - gain_gold=round(lost_gold*0.9); - if(target.type=="merchant") lost_xp=0; - if(gameplay!="hardcore" && gameplay!="test" && is_same(attacker,target,1)) lost_gold=gain_gold=0,lost_xp=0; + if (gameplay == "hardcore") { + lost_gold = round(target.gold * 0.8); + lost_xp = target.xp; + for (var i = 1; i <= B.hlevel_loss; i++) { + if (target.level - i > 0) { + lost_xp += G.levels[target.level - i + ""]; + } + } + target.level = max(1, target.level - B.hlevel_loss); + } - if(!is_same(attacker,target,1)) attacker.kills++; + lost_gold = min(target.gold, lost_gold); + gain_gold = round(lost_gold * 0.9); - if(mode.log_pvp) appengine_log("pvp",attacker.name+" pwned "+target.name+" For "+lost_gold+" Gold "+lost_xp+" XP"); - pwns[(pend++)%200]=[attacker.name,target.name]; + if (target.type == "merchant") { + lost_xp = 0; + } + if (gameplay != "hardcore" && gameplay != "test" && is_same(attacker, target, 1)) { + lost_gold = gain_gold = 0; + lost_xp = 0; + } + if (!is_same(attacker, target, 1)) { + attacker.kills++; + } - target.socket.emit("game_log",{message:"Slain by "+attacker.name,color:"#F12F02"}); - var lost_shells=0,psize=1; - if(lost_xp) - for(var i=0;i10) - { - attacker.socket.emit("game_response",{response:"attack_failed",id:target.id,reason:"level"}); - return {failed:true,reason:"level_gap",place:atype,id:target.id}; + if ( + mode.pvp_level_gap && + attacker.is_player && + target.is_player && + !info.positive && + abs(attacker.level - target.level) > 10 + ) { + attacker.socket.emit("game_response", { response: "attack_failed", id: target.id, reason: "level" }); + return { failed: true, reason: "level_gap", place: atype, id: target.id }; } - var dist=distance(attacker,target),range=0; - var def={hid:attacker.id,source:atype,projectile:null}; + var dist = distance(attacker, target); + var range = 0; + var def = { hid: attacker.id, source: atype, projectile: null }; // PROJECTILE LOGIC - if(attacker.is_monster) def.projectile=G.monsters[attacker.type].projectile||"stone"; - if(attacker.is_player && G.classes[attacker.type].projectile) def.projectile=G.classes[attacker.type].projectile; - if(attacker.projectile) def.projectile=attacker.projectile; - if(attacker.slots && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].projectile) def.projectile=G.items[attacker.slots.mainhand.name].projectile; - if(attacker.tskin=="konami") def.projectile="stone_k"; - if((atype!="attack" || !def.projectile || atype=="heal") && G.skills[atype].projectile) def.projectile=G.skills[atype].projectile; + if (attacker.is_monster) { + def.projectile = G.monsters[attacker.type].projectile || "stone"; + } + if (attacker.is_player && G.classes[attacker.type].projectile) { + def.projectile = G.classes[attacker.type].projectile; + } + if (attacker.projectile) { + def.projectile = attacker.projectile; + } + if (attacker.slots && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].projectile) { + def.projectile = G.items[attacker.slots.mainhand.name].projectile; + } + if (attacker.tskin == "konami") { + def.projectile = "stone_k"; + } + if ((atype != "attack" || !def.projectile || atype == "heal") && G.skills[atype].projectile) { + def.projectile = G.skills[atype].projectile; + } // DAMAGE TYPE LOGIC - if(attacker.is_monster && G.monsters[attacker.type].damage_type) info.damage_type=G.monsters[attacker.type].damage_type; - if(attacker.is_player && G.classes[attacker.type].damage_type) info.damage_type=G.classes[attacker.type].damage_type; - if(attacker.is_player && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].damage_type) info.damage_type=G.items[attacker.slots.mainhand.name].damage_type; - if(atype!="attack" && G.skills[atype].damage_type) info.damage_type=G.skills[atype].damage_type; + if (attacker.is_monster && G.monsters[attacker.type].damage_type) { + info.damage_type = G.monsters[attacker.type].damage_type; + } + if (attacker.is_player && G.classes[attacker.type].damage_type) { + info.damage_type = G.classes[attacker.type].damage_type; + } + if (attacker.is_player && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].damage_type) { + info.damage_type = G.items[attacker.slots.mainhand.name].damage_type; + } + if (atype != "attack" && G.skills[atype].damage_type) { + info.damage_type = G.skills[atype].damage_type; + } // PROCS - if(!attacker.is_player || G.skills[atype].procs) info.procs=true; + if (!attacker.is_player || G.skills[atype].procs) { + info.procs = true; + } // HEAL / POSITIVE - if(G.skills[atype].heal || attacker.is_player && attacker.slots.mainhand && attacker.slots.mainhand.name=="cupid") - info.heal=true,info.positive=true; - - if(attacker.is_player && target.is_player && !is_in_pvp(target,true) && !info.positive || target.npc) - { - attacker.socket.emit("game_response",{response:"attack_failed",id:target.id}); - return {failed:true,reason:"no_pvp",place:atype,id:target.id} + if ( + G.skills[atype].heal || + (attacker.is_player && attacker.slots.mainhand && attacker.slots.mainhand.name == "cupid") + ) { + info.heal = true; + info.positive = true; } - // COMMON RANGE CHECKS - if(G.skills[atype].use_range) range=attacker.range; - if(G.skills[atype].range) range=G.skills[atype].range; + if ((attacker.is_player && target.is_player && !is_in_pvp(target, true) && !info.positive) || target.npc) { + attacker.socket.emit("game_response", { response: "attack_failed", id: target.id }); + return { failed: true, reason: "no_pvp", place: atype, id: target.id }; + } + + // COMMON RANGE CHECKS + if (G.skills[atype].use_range) { + range = attacker.range; + } + if (G.skills[atype].range) { + range = G.skills[atype].range; + } // DAMAGE - if(G.skills[atype].damage) attack=G.skills[atype].damage; + if (G.skills[atype].damage) { + attack = G.skills[atype].damage; + } // SKILL MP - if(G.skills[atype].mp) mp_cost=G.skills[atype].mp; - - if(attacker.is_player && range && !attacker.is_npc) - { - if(dist>range+attacker.xrange) - { - attacker.socket.emit("game_response",{response:"too_far",id:target.id,dist:dist}); - return {failed:true,reason:"too_far",place:atype,id:target.id,dist:dist}; - } - if(dist>range) attacker.xrange+=range-dist; + if (G.skills[atype].mp) { + mp_cost = G.skills[atype].mp; } - if(info.procs && attacker.s.poisonous) info.conditions.push("poisoned"); + if (attacker.is_player && range && !attacker.is_npc) { + if (dist > range + attacker.xrange) { + attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); + return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; + } + if (dist > range) { + attacker.xrange += range - dist; + } + } - if(atype=="heal") - { - attack=attacker.heal||attacker.attack; - mp_cost=attacker.mp_cost; - } - else if(attacker.is_player && (atype=="attack" || atype=="3shot" || atype=="5shot" || atype=="cleave" || atype=="shadowstrike")) - { - var mp_mult=1,att_mult=1; - if(atype=="3shot") mp_mult=0,att_mult=0.7; - if(atype=="5shot") mp_mult=0,att_mult=0.5; - if(atype=="cleave") mp_mult=0.02,info.lines=false,att_mult=0.1+Math.random()*0.8,def.aoe=true,attacker.first=true; - if(atype=="shadowstrike") mp_mult=0,info.lines=false,att_mult=(0.2+Math.random()*1.2)*((Math.random()<0.05)&&12||1),def.aoe=true; - if(attacker.mp=80) attack=800; - else if(attacker.level>=72) attack=720; - else if(attacker.level>=60) attack=600; - else attack=400; - } - else if(atype=="selfheal") - { - if(attacker.level>=80) attack=800; - else if(attacker.level>=72) attack=720; - else if(attacker.level>=60) attack=600; - else attack=400; - } - else if(atype=="taunt") - { - if(target.is_monster && target.target && get_player(target.target) && is_same(attacker,get_player(target.target),1)) - { - stop_pursuit(target,{redirect:true,cause:"taunt redirect"}); - target_player(target,attacker); - } - } - else if(atype=="curse") - { - if(distance(attacker,target)>min(200,attacker.range*5+20)) - { - attacker.socket.emit("game_response",{response:"too_far",id:target.id,dist:dist}); - return {failed:true,reason:"too_far",place:atype,id:target.id,dist:dist} - } - attack=0; - info.conditions.push("cursed"); - } - else if(atype=="burst") - { - if(attacker.mp<1) - { - attacker.socket.emit("game_response",{response:"no_mp"}); - return {failed:true,reason:"no_mp",place:atype,id:target.id} - } - attack=attacker.mp*G.skills.burst.ratio; - mp_cost=attacker.mp; - } - else if(atype=="cburst") - { - var mp_cutoff=attacker.next_mp,mp=attacker.next_mp; - if(atype=="cburst") mp=mp_cutoff=attacker.next_mp; - if(attacker.mp3*attacker.range+20) - { - attacker.socket.emit("game_response",{response:"too_far",id:target.id,dist:dist}); - return {failed:true,reason:"too_far",place:atype,id:target.id,dist:dist}; - } - attack=attacker.attack*1.5; - // if(attacker.slots.mainhand && attacker.slots.mainhand.name=="cupid") info.heal=info.positive=true,def.projectile=G.items.cupid.projectile; - } - else if(atype=="snowball") - { - if(target.is_monster && attacker.a.freeze && attacker.a.freeze.attr0) - attack+=10*attacker.a.freeze.attr0*attacker.a.freeze.attr0; - if(target.is_monster || is_in_pvp(attacker,true)) - info.conditions.push("frozen"); - } - else if(atype=="quickpunch" || atype=="quickstab" || atype=="smash") - { - attack=attacker.attack*G.skills[atype].damage_multiplier; - } - else if(atype=="mentalburst") - { - if(attacker.in!=target.in || distance(attacker,target,true)>attacker.range*G.skills.mentalburst.range_multiplier+G.skills.mentalburst.range_bonus) - { - attacker.socket.emit("game_response",{response:"too_far",id:target.id,dist:dist}); - return {failed:true,reason:"too_far",place:atype,id:target.id,dist:dist} - } - attack=attacker.attack*G.skills[atype].damage_multiplier; - } - else if(atype=="poisonarrow") - { + if (info.procs && attacker.s.poisonous) { info.conditions.push("poisoned"); } - else if(attacker.is_monster) - { - var rng=parseInt(Math.random()*100-50); - if(attacker.s.poisonous) info.conditions.push("poisoned"); - attacker.last.attack=future_ms(rng); + + if (atype == "heal") { + attack = attacker.heal || attacker.attack; + mp_cost = attacker.mp_cost; + } else if ( + attacker.is_player && + (atype == "attack" || atype == "3shot" || atype == "5shot" || atype == "cleave" || atype == "shadowstrike") + ) { + var mp_mult = 1; + var att_mult = 1; + if (atype == "3shot") { + mp_mult = 0; + att_mult = 0.7; + } + if (atype == "5shot") { + mp_mult = 0; + att_mult = 0.5; + } + if (atype == "cleave") { + mp_mult = 0.02; + info.lines = false; + att_mult = 0.1 + Math.random() * 0.8; + def.aoe = true; + attacker.first = true; + } + if (atype == "shadowstrike") { + mp_mult = 0; + info.lines = false; + att_mult = (0.2 + Math.random() * 1.2) * ((Math.random() < 0.05 && 12) || 1); + def.aoe = true; + } + if (attacker.mp < attacker.mp_cost * mp_mult) { + attacker.socket.emit("game_response", { response: "no_mp" }); + return { failed: true, reason: "no_mp", place: atype, id: target.id }; + } + mp_cost = parseInt(attacker.mp_cost * mp_mult); + attack = attacker.attack * att_mult; + } else if (atype == "piercingshot") { + info.apiercing = 500; + attack = attacker.attack * 0.75; + } else if (atype == "partyheal") { + if (attacker.level >= 80) { + attack = 800; + } else if (attacker.level >= 72) { + attack = 720; + } else if (attacker.level >= 60) { + attack = 600; + } else { + attack = 400; + } + } else if (atype == "selfheal") { + if (attacker.level >= 80) { + attack = 800; + } else if (attacker.level >= 72) { + attack = 720; + } else if (attacker.level >= 60) { + attack = 600; + } else { + attack = 400; + } + } else if (atype == "taunt") { + if ( + target.is_monster && + target.target && + get_player(target.target) && + is_same(attacker, get_player(target.target), 1) + ) { + stop_pursuit(target, { redirect: true, cause: "taunt redirect" }); + target_player(target, attacker); + } + } else if (atype == "curse") { + if (distance(attacker, target) > min(200, attacker.range * 5 + 20)) { + attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); + return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; + } + attack = 0; + info.conditions.push("cursed"); + } else if (atype == "burst") { + if (attacker.mp < 1) { + attacker.socket.emit("game_response", { response: "no_mp" }); + return { failed: true, reason: "no_mp", place: atype, id: target.id }; + } + attack = attacker.mp * G.skills.burst.ratio; + mp_cost = attacker.mp; + } else if (atype == "cburst") { + var mp_cutoff = attacker.next_mp; + var mp = attacker.next_mp; + if (atype == "cburst") { + mp = mp_cutoff = attacker.next_mp; + } + if (attacker.mp < mp_cutoff) { + attacker.socket.emit("game_response", { response: "no_mp" }); + return { failed: true, reason: "no_mp", place: atype, id: target.id }; + } + attack = mp * G.skills.cburst.ratio; + mp_cost = mp; + attacker.first = true; + } else if (atype == "purify") { + def.purify = true; + } else if (atype == "frostball") { + info.conditions.push("frozen"); + } else if (atype == "fireball") { + info.conditions.push("burned"); + } else if (atype == "supershot") { + if (attacker.in != target.in || distance(attacker, target, true) > 3 * attacker.range + 20) { + attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); + return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; + } + attack = attacker.attack * 1.5; + // if(attacker.slots.mainhand && attacker.slots.mainhand.name=="cupid") info.heal=info.positive=true,def.projectile=G.items.cupid.projectile; + } else if (atype == "snowball") { + if (target.is_monster && attacker.a.freeze && attacker.a.freeze.attr0) { + attack += 10 * attacker.a.freeze.attr0 * attacker.a.freeze.attr0; + } + if (target.is_monster || is_in_pvp(attacker, true)) { + info.conditions.push("frozen"); + } + } else if (atype == "quickpunch" || atype == "quickstab" || atype == "smash") { + attack = attacker.attack * G.skills[atype].damage_multiplier; + } else if (atype == "mentalburst") { + if ( + attacker.in != target.in || + distance(attacker, target, true) > + attacker.range * G.skills.mentalburst.range_multiplier + G.skills.mentalburst.range_bonus + ) { + attacker.socket.emit("game_response", { response: "too_far", id: target.id, dist: dist }); + return { failed: true, reason: "too_far", place: atype, id: target.id, dist: dist }; + } + attack = attacker.attack * G.skills[atype].damage_multiplier; + } else if (atype == "poisonarrow") { + info.conditions.push("poisoned"); + } else if (attacker.is_monster) { + var rng = parseInt(Math.random() * 100 - 50); + if (attacker.s.poisonous) { + info.conditions.push("poisoned"); + } + attacker.last.attack = future_ms(rng); } - if(atype!="attack" && target.immune && (!G.skills[atype] || !G.skills[atype].pierces_immunity)) - { - disappearing_text(target.socket,target,"IMMUNE!",{xy:1,color:"evade",nv:1,from:attacker.id}); - return {failed:true,reason:"skill_immune",place:atype,id:target.id}; + if (atype != "attack" && target.immune && (!G.skills[atype] || !G.skills[atype].pierces_immunity)) { + disappearing_text(target.socket, target, "IMMUNE!", { xy: 1, color: "evade", nv: 1, from: attacker.id }); + return { failed: true, reason: "skill_immune", place: atype, id: target.id }; } - if(!info.positive && (attacker.is_player && G.maps[attacker.map].safe || !mode.friendly_fire && (!attacker.team || attacker.team!=target.team) && (attacker.party && attacker.party==target.party || attacker.guild && attacker.guild==target.guild)) || attacker.map=="duelland" && (!attacker.duel || !target.duel)) - { - attacker.socket.emit("game_response",{response:"friendly",id:target.id}); - return {failed:true,reason:"friendly",place:atype,id:target.id} + if ( + (!info.positive && + ((attacker.is_player && G.maps[attacker.map].safe) || + (!mode.friendly_fire && + (!attacker.team || attacker.team != target.team) && + ((attacker.party && attacker.party == target.party) || + (attacker.guild && attacker.guild == target.guild))))) || + (attacker.map == "duelland" && (!attacker.duel || !target.duel)) + ) { + attacker.socket.emit("game_response", { response: "friendly", id: target.id }); + return { failed: true, reason: "friendly", place: atype, id: target.id }; } - direction_logic(attacker,target); + direction_logic(attacker, target); - if(mp_cost && attacker.first && !attacker.is_npc) consume_mp(attacker,mp_cost,atype!="attack"&&atype!="heal"&&target); - attacker.first=false; + if (mp_cost && attacker.first && !attacker.is_npc) { + consume_mp(attacker, mp_cost, atype != "attack" && atype != "heal" && target); + } + attacker.first = false; - if(attacker.is_player) - { - attacker.c={}; - attacker.to_resend="u+cid"; - if(attacker.p && attacker.p.stand) attacker.p.stand=false; + if (attacker.is_player) { + attacker.c = {}; + attacker.to_resend = "u+cid"; + if (attacker.p && attacker.p.stand) { + attacker.p.stand = false; + } step_out_of_invis(attacker); - if(attacker.type=="merchant") - { - var gold=parseInt(attack*2); - attack=parseInt(min(attack,attacker.gold/2)); - attacker.gold=max(0,attacker.gold-gold); - attacker.to_resend+="+reopen"; + if (attacker.type == "merchant") { + var gold = parseInt(attack * 2); + attack = parseInt(min(attack, attacker.gold / 2)); + attacker.gold = max(0, attacker.gold - gold); + attacker.to_resend += "+reopen"; } } - - if(info.procs) - ["crit","critdamage","explosion","blast","lifesteal","manasteal"].forEach(function(p){if(attacker[p]) info[p]=attacker[p];}); - ["apiercing","rpiercing","miss"].forEach(function(p){if(attacker[p]) info[p]=attacker[p];}); - if(info.procs && attacker.a.freeze && Math.random()0 && !info.heal) - { - add_pdps(target,attacker,attack*B.dps_tank_mult); // "tank" - if(attacker.cooperative) add_coop_points(attacker,target,attack*B.dps_tank_mult); +function complete_attack(attacker, target, info) { + var defense = "armor"; + var pierce = "apiercing"; + var combo = 1; + var combo_m = 1; + var atype = info.atype; + var evade = false; + var first = true; + var attack = info.attack; + var o_attack; + var i_attack = info.attack; + var def = info.def; + if (G.monsters[attacker.type] && G.monsters[attacker.type].good) { + info.heal = info.positive = true; } - if(attacker.is_monster && attack>0 && !info.heal) - { - attacker.outgoing+=min(target.hp,attack); + var change = false; + var targets = [[target, "normal"]]; + var otarget = target; + var events = []; + info.action.map = attacker.map; + info.action.in = attacker.in; + + if (info.damage_type == "pure" || target === attacker) { + defense = "none_existent"; + pierce = "non_existent"; + info.apiercing = 0; + } else if (info.damage_type == "magical") { + defense = "resistance"; + pierce = "rpiercing"; + info.apiercing = 0; + } else { + info.damage_type = "physical"; } - if(target.reflection && defense=="resistance" && Math.random()*100 0 && !info.heal) { + add_pdps(target, attacker, attack * B.dps_tank_mult); // "tank" + if (attacker.cooperative) { + add_coop_points(attacker, target, attack * B.dps_tank_mult); + } + } + if (attacker.is_monster && attack > 0 && !info.heal) { + attacker.outgoing += min(target.hp, attack); + } + + if ( + target.reflection && + defense == "resistance" && + Math.random() * 100 < target.reflection && + !info.heal && + attacker != target + ) { + var pid = randomStr(6); + var eta = 0; + var opid = info.action.pid; // info.attack=ceil(attack*damage_multiplier(attacker.resistance||0))||1; - info.attack=ceil(attack*(0.90+(attack&&(Math.random()*0.2)||0))); // A pure reflection was requested [29/03/22] - if(!info.action.instant) eta=1000*distance(target,attacker,true)/G.projectiles[info.action.projectile].speed; - info.target=attacker; - info.attacker=target; - info.eta=future_ms(eta); - projectiles[pid]=info; - info.action.pid=pid; - info.action.target=attacker.id; - info.action.attacker=target.id; - info.action.x=attacker.x; info.action.y=attacker.y; - info.action.reflect=info.attack; - info.action.m=attacker.m; + info.attack = ceil(attack * (0.9 + ((attack && Math.random() * 0.2) || 0))); // A pure reflection was requested [29/03/22] + if (!info.action.instant) { + eta = (1000 * distance(target, attacker, true)) / G.projectiles[info.action.projectile].speed; + } + info.target = attacker; + info.attacker = target; + info.eta = future_ms(eta); + projectiles[pid] = info; + info.action.pid = pid; + info.action.target = attacker.id; + info.action.attacker = target.id; + info.action.x = attacker.x; + info.action.y = attacker.y; + info.action.reflect = info.attack; + info.action.m = attacker.m; - if(attacker.is_player && target.is_monster || info.reflections) info.reflections=(info.reflections||0)+1; - if(info.reflections>=4 && attacker.is_player) item_achievement_increment(attacker,attacker.slots.chest,"reflector"); - if(info.reflections>=4 && target.is_player) item_achievement_increment(target,target.slots.chest,"reflector"); + if ((attacker.is_player && target.is_monster) || info.reflections) { + info.reflections = (info.reflections || 0) + 1; + } + if (info.reflections >= 4 && attacker.is_player) { + item_achievement_increment(attacker, attacker.slots.chest, "reflector"); + } + if (info.reflections >= 4 && target.is_player) { + item_achievement_increment(target, target.slots.chest, "reflector"); + } - xy_emit(target,"hit",{pid:def.pid,hid:attacker.id,id:target.id,damage:0,reflect:info.attack},attacker.id); - return xy_emit(target,"action",info.action,attacker.id); + xy_emit( + target, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, reflect: info.attack }, + attacker.id, + ); + return xy_emit(target, "action", info.action, attacker.id); } - if(attacker==target && !target.dead); // reflect after dead fix [04/02/23] - else if(target.evasion && defense=="armor" && Math.random()*10072*(info.heal&&1.5||1)) - return xy_emit(info.action,"hit",{pid:def.pid,hid:attacker.id,id:target.id,damage:0,avoid:true,x:info.action.x,y:info.action.y,map:info.attacker.map,in:info.attacker.in},attacker.id); - - if(info.positive || !info.procs) combo=0; - if(combo && target.targets>3) - { - if(!target.last_combo || ssince(target.last_combo)>5) target.combo=1; - combo+=target.combo; - target.last_combo=new Date(); - target.combo+=1; - def.mobbing=target.combo; + if (attacker == target && !target.dead) { + } // reflect after dead fix [04/02/23] + else if (target.evasion && defense == "armor" && Math.random() * 100 < target.evasion) { + return xy_emit( + info.action, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, evade: true }, + attacker.id, + ); + } else if ( + target.dc || + target.dead || + (attacker.miss && Math.random() * 100 < attacker.miss) || + (target.avoidance && Math.random() * 100 < target.avoidance) + ) { + return xy_emit( + info.action, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, miss: true }, + attacker.id, + ); + } else if ( + target.m != info.action.m || + point_distance(target.x, target.y, info.action.x, info.action.y) > 72 * ((info.heal && 1.5) || 1) + ) { + return xy_emit( + info.action, + "hit", + { + pid: def.pid, + hid: attacker.id, + id: target.id, + damage: 0, + avoid: true, + x: info.action.x, + y: info.action.y, + map: info.attacker.map, + in: info.attacker.in, + }, + attacker.id, + ); } - if(combo && target.is_player && instances[target.in].pmap[target.last_hash] && Object.keys(instances[target.in].pmap[target.last_hash]).length>1) - { - var combo_check=false; - for(var id in instances[target.in].pmap[target.last_hash]) - { - var ntarget=instances[target.in].pmap[target.last_hash][id]; - if(target.id==ntarget.id || is_invinc(ntarget) || !is_same(target,ntarget,2) && !is_in_pvp(target) || attacker.id==ntarget.id) continue; //is_same(attacker,ntarget,1) + + if (info.positive || !info.procs) { + combo = 0; + } + if (combo && target.targets > 3) { + if (!target.last_combo || ssince(target.last_combo) > 5) { + target.combo = 1; + } + combo += target.combo; + target.last_combo = new Date(); + target.combo += 1; + def.mobbing = target.combo; + } + if ( + combo && + target.is_player && + instances[target.in].pmap[target.last_hash] && + Object.keys(instances[target.in].pmap[target.last_hash]).length > 1 + ) { + var combo_check = false; + for (var id in instances[target.in].pmap[target.last_hash]) { + var ntarget = instances[target.in].pmap[target.last_hash][id]; + if ( + target.id == ntarget.id || + is_invinc(ntarget) || + (!is_same(target, ntarget, 2) && !is_in_pvp(target)) || + attacker.id == ntarget.id + ) { + continue; + } //is_same(attacker,ntarget,1) //!is_same(target,ntarget,1) && !is_in_pvp(target) to prevent people killing other people in non-pvp servers //!is_same(target,ntarget,2) makes people kill others in coop fights - combo_check=true; break; + combo_check = true; + break; } - if(combo_check) - { - targets=[]; combo=0; - for(var id in instances[target.in].pmap[target.last_hash]) - { - var ntarget=instances[target.in].pmap[target.last_hash][id]; - if(is_invinc(ntarget) || attacker.id==ntarget.id || !is_same(target,ntarget,2)) continue; //is_same(attacker,ntarget,1) - targets.push([ntarget,"stack"]); - if(!ntarget.last_combo || ssince(ntarget.last_combo)>5) ntarget.combo=1; - combo+=ntarget.combo; - ntarget.last_combo=new Date(); - ntarget.combo+=1; + if (combo_check) { + targets = []; + combo = 0; + for (var id in instances[target.in].pmap[target.last_hash]) { + var ntarget = instances[target.in].pmap[target.last_hash][id]; + if (is_invinc(ntarget) || attacker.id == ntarget.id || !is_same(target, ntarget, 2)) { + continue; + } //is_same(attacker,ntarget,1) + targets.push([ntarget, "stack"]); + if (!ntarget.last_combo || ssince(ntarget.last_combo) > 5) { + ntarget.combo = 1; + } + combo += ntarget.combo; + ntarget.last_combo = new Date(); + ntarget.combo += 1; } - if(targets.length>1) - { - def.stacked=[]; - targets.forEach(function(t){ def.stacked.push(t[0].id); }) + if (targets.length > 1) { + def.stacked = []; + targets.forEach(function (t) { + def.stacked.push(t[0].id); + }); } } } - - if(combo>10) combo_m=combo/4.0; - else if(combo>1) combo_m=[1,1.6,1.62,1.64,1.70,1.72,1.75,1.8,1.9,2,2,2,2][combo]; - combo_m=min(combo_m,max(300/attack,1.2)); // previously 2.4 - if(!info.positive && (info.explosion && info.damage_type=="physical" || info.blast && info.damage_type=="magical")) - { - var intensity=info.blast,defense="resistance"; - if(info.damage_type=="physical") intensity=info.explosion,defense="armor"; - var radius=intensity/3.6; - if(is_in_pvp(attacker) || attacker.is_monster) - for(var id in instances[target.in].players) - { - var target=instances[target.in].players[id]; - if(target.npc) continue; - if(target.id!=otarget.id && distance(target,otarget) 10) { + combo_m = combo / 4.0; + } else if (combo > 1) { + combo_m = [1, 1.6, 1.62, 1.64, 1.7, 1.72, 1.75, 1.8, 1.9, 2, 2, 2, 2][combo]; + } + combo_m = min(combo_m, max(300 / attack, 1.2)); // previously 2.4 + + if ( + !info.positive && + ((info.explosion && info.damage_type == "physical") || (info.blast && info.damage_type == "magical")) + ) { + var intensity = info.blast; + var defense = "resistance"; + if (info.damage_type == "physical") { + intensity = info.explosion; + defense = "armor"; + } + var radius = intensity / 3.6; + if (is_in_pvp(attacker) || attacker.is_monster) { + for (var id in instances[target.in].players) { + var target = instances[target.in].players[id]; + if (target.npc) { continue; } - if(target.id!=otarget.id && distance(target,otarget)0 && target.s.invincible) attack=0; - if(attacker.tskin=="konami" && target.type!=attacker.p.target_lock) attack=0; + if (target["1hp"]) { + attack = (def.crit && 2) || 1; + } + if (attack > 0 && target.s.invincible) { + attack = 0; + } + if (attacker.tskin == "konami" && target.type != attacker.p.target_lock) { + attack = 0; + } - o_attack=attack=ceil(attack); + o_attack = attack = ceil(attack); - if(info.conditions.includes("frozen") && !target.immune && target.hp>attack) - { - if(Math.random() attack) { + if (Math.random() < target.fzresistance / 100.0) { + xy_emit(target, "ui", { type: "freeze_resist", id: target.id }); + } else { + add_condition(target, "frozen"); + disappearing_text(target.socket, target, "FREEZE!", { xy: 1, size: "huge", color: "freeze", nv: 1 }); //target.is_player&&"huge"||undefined } } - if(info.conditions.includes("burned") && !target.immune && target.hp>attack) - add_condition(target,"burned",{divider:attacker.a && attacker.a.burn && attacker.a.burn.unlimited && 1.5, - fid:attacker.id,f:attacker.name||G.monsters[attacker.type].name,attack:attack}) + if (info.conditions.includes("burned") && !target.immune && target.hp > attack) { + add_condition(target, "burned", { + divider: attacker.a && attacker.a.burn && attacker.a.burn.unlimited && 1.5, + fid: attacker.id, + f: attacker.name || G.monsters[attacker.type].name, + attack: attack, + }); + } - if(info.conditions.includes("woven") && !target.immune) - add_condition(target,"woven"); + if (info.conditions.includes("woven") && !target.immune) { + add_condition(target, "woven"); + } - if(info.conditions.includes("stunned") && target.hp>attack && add_condition(target,"stunned",{duration:2000})) - disappearing_text(target.socket,target,"STUN!",{xy:1,size:"huge",color:"stun",nv:1}); //target.is_player&&"huge"||undefined - - if(info.procs && target.a.putrid) - { - add_condition(attacker,"poisoned"); - add_condition(attacker,"cursed"); - change=true; + if ( + info.conditions.includes("stunned") && + target.hp > attack && + add_condition(target, "stunned", { duration: 2000 }) + ) { + disappearing_text(target.socket, target, "STUN!", { xy: 1, size: "huge", color: "stun", nv: 1 }); + } //target.is_player&&"huge"||undefined + + if (info.procs && target.a.putrid) { + add_condition(attacker, "poisoned"); + add_condition(attacker, "cursed"); + change = true; } - info.conditions.forEach(function(c){ - if(["frozen","burned","woven","stunned"].includes(c)) return; - if(target.hp>attack && !target.immune) - add_condition(target,c); - }) - if(info.procs && attacker.a.sugarrush && Math.random()<0.0025) - { - def.trigger="sugarrush"; - add_condition(attacker,"sugarrush"); - disappearing_text(attacker.socket,attacker,"SUGAR RUSH!",{xy:1,size:"huge",color:"sugar",nv:1}); //target.is_player&&"huge"||undefined + info.conditions.forEach(function (c) { + if (["frozen", "burned", "woven", "stunned"].includes(c)) { + return; + } + if (target.hp > attack && !target.immune) { + add_condition(target, c); + } + }); + if (info.procs && attacker.a.sugarrush && Math.random() < 0.0025) { + def.trigger = "sugarrush"; + add_condition(attacker, "sugarrush"); + disappearing_text(attacker.socket, attacker, "SUGAR RUSH!", { xy: 1, size: "huge", color: "sugar", nv: 1 }); //target.is_player&&"huge"||undefined } - if(attacker.s.invis) // && target.is_player - { - def.sneak=true; - disappearing_text(target.socket,target,"SNEAK!",{xy:1,size:"huge",color:"sneak",nv:1}); + if (attacker.s.invis) { + // && target.is_player + def.sneak = true; + disappearing_text(target.socket, target, "SNEAK!", { xy: 1, size: "huge", color: "sneak", nv: 1 }); } - if(info.damage_type=="pure") attack=ceil(info.first_attack); - if(target["1hp"]) attack=def.crit&&2||1; - } - else - { - attack=o_attack; - if(target_def[1]=="splash") attack=ceil(attack*target_def[2]); - if(target["1hp"]) def.damage=o_attack=attack=1; - } - if(attack>=1) - { - target.last.attacked=new Date(); - if(target.is_monster && attacker.is_player) target.points[attacker.name]=(target.points[attacker.name]||0)+1; - } - if(atype=="deepfreeze" && add_condition(target,"deepfreezed")) - def.deepfreeze=true; - - def.damage=attack; - if(info.lifesteal) - { - var hp=ceil(min(attack,target.hp)*info.lifesteal/100.0); - attacker.hp=min(attacker.max_hp,attacker.hp+hp); change=true; - if(hp) def.lifesteal=hp; - } - if(info.manasteal) - { - var mp=ceil(min(attack,target.hp)*info.manasteal/100.0); - if(target.mp!==undefined) - { - mp=min(target.mp,mp); - target.mp=max(0,target.mp-mp); + if (info.damage_type == "pure") { + attack = ceil(info.first_attack); } - else - mp=0; - if(attacker.mp!==undefined) - { - attacker.mp=min(attacker.max_mp||0,attacker.mp+mp); change=true; + if (target["1hp"]) { + attack = (def.crit && 2) || 1; + } + } else { + attack = o_attack; + if (target_def[1] == "splash") { + attack = ceil(attack * target_def[2]); + } + if (target["1hp"]) { + def.damage = o_attack = attack = 1; } - if(mp) def.manasteal=mp; } - if(G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.gold) - { + if (attack >= 1) { + target.last.attacked = new Date(); + if (target.is_monster && attacker.is_player) { + target.points[attacker.name] = (target.points[attacker.name] || 0) + 1; + } + } + if (atype == "deepfreeze" && add_condition(target, "deepfreezed")) { + def.deepfreeze = true; + } + + def.damage = attack; + if (info.lifesteal) { + var hp = ceil((min(attack, target.hp) * info.lifesteal) / 100.0); + attacker.hp = min(attacker.max_hp, attacker.hp + hp); + change = true; + if (hp) { + def.lifesteal = hp; + } + } + if (info.manasteal) { + var mp = ceil((min(attack, target.hp) * info.manasteal) / 100.0); + if (target.mp !== undefined) { + mp = min(target.mp, mp); + target.mp = max(0, target.mp - mp); + } else { + mp = 0; + } + if (attacker.mp !== undefined) { + attacker.mp = min(attacker.max_mp || 0, attacker.mp + mp); + change = true; + } + if (mp) { + def.manasteal = mp; + } + } + if (G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.gold) { //var gold=min(target.gold,parseInt(ceil(target.level*target.level/10))); - var gold=parseInt(Math.random()*12)+1; - target.gold=max(0,target.gold-gold); - attacker.extra_gold=(attacker.extra_gold||0)+gold; - def.goldsteal=gold; + var gold = parseInt(Math.random() * 12) + 1; + target.gold = max(0, target.gold - gold); + attacker.extra_gold = (attacker.extra_gold || 0) + gold; + def.goldsteal = gold; } } - var original=target.hp; - if(target.s.mshield && target.mp>200 && attack>0) - { + var original = target.hp; + if (target.s.mshield && target.mp > 200 && attack > 0) { // console.log("HERE MPSHIELD!") - var max_mp=target.mp-200,damage_per_mp=1.5; - if(target.level>99) damage_per_mp=3; - else if(target.level>89) damage_per_mp=2.4; - else if(target.level>79) damage_per_mp=2; - else if(target.level>69) damage_per_mp=1.75; - var max_hp=ceil(max_mp*damage_per_mp); - if(max_hp 99) { + damage_per_mp = 3; + } else if (target.level > 89) { + damage_per_mp = 2.4; + } else if (target.level > 79) { + damage_per_mp = 2; + } else if (target.level > 69) { + damage_per_mp = 1.75; } - else - { - def.mp_damage=parseInt(attack/damage_per_mp); - attack=0; - target.mp-=def.mp_damage; + var max_hp = ceil(max_mp * damage_per_mp); + if (max_hp < attack) { + def.mp_damage = max_mp; + attack -= max_hp; + target.mp = 200; + } else { + def.mp_damage = parseInt(attack / damage_per_mp); + attack = 0; + target.mp -= def.mp_damage; } - change=true; + change = true; } - target.hp=min(target.hp-attack,target.max_hp); // both for damage and heal - var net=original-max(0,target.hp); - if(target.hp<=0) - { - def.kill=true; - if(G.skills[atype].kill_buff) add_condition(attacker,G.skills[atype].kill_buff); + target.hp = min(target.hp - attack, target.max_hp); // both for damage and heal + var net = original - max(0, target.hp); + if (target.hp <= 0) { + def.kill = true; + if (G.skills[atype].kill_buff) { + add_condition(attacker, G.skills[atype].kill_buff); + } } - if(target.dreturn && i_attack>0 && first && attacker.range<75 && info.damage_type=="physical" && !attacker["1hp"]) // dreturn happens at every hit - { - def.dreturn=ceil(i_attack*target.dreturn/100.0); - if(attacker.is_monster) attacker.u=true,attacker.cid++; - attacker.hp=max(attacker.hp-def.dreturn,0); + if ( + target.dreturn && + i_attack > 0 && + first && + attacker.range < 75 && + info.damage_type == "physical" && + !attacker["1hp"] + ) { + // dreturn happens at every hit + def.dreturn = ceil((i_attack * target.dreturn) / 100.0); + if (attacker.is_monster) { + attacker.u = true; + attacker.cid++; + } + attacker.hp = max(attacker.hp - def.dreturn, 0); } // if(target.is_player && net>0) target.s.damage_received={amount:target.s.damage_received&&(target.s.damage_received.amount+net)||net,ms:10000}; - if(attacker.is_player) - { - if(net>0) // "attack" - { - if(attacker.t) attacker.t.mdamage+=net; - add_pdps(attacker,target,net); + if (attacker.is_player) { + if (net > 0) { + // "attack" + if (attacker.t) { + attacker.t.mdamage += net; + } + add_pdps(attacker, target, net); + } // "heal" + else { + add_pdps(attacker, target, -net * B.dps_heal_mult); } - else // "heal" - add_pdps(attacker,target,-net*B.dps_heal_mult); - var m=target,mnet=net; - if(net<0 && target.s && target.s.coop) - { - m=instances[attacker.in].monsters[target.s.coop.id]; - mnet=-net*B.dps_heal_mult; - if(m && m.attack<120) mnet/=100; // dirty fix + var m = target; + var mnet = net; + if (net < 0 && target.s && target.s.coop) { + m = instances[attacker.in].monsters[target.s.coop.id]; + mnet = -net * B.dps_heal_mult; + if (m && m.attack < 120) { + mnet /= 100; + } // dirty fix // console.log("coop heal: "+m) } - if(target.master) - { - m=instances[attacker.in].monsters[target.master]; + if (target.master) { + m = instances[attacker.in].monsters[target.master]; } - if(m && m.is_monster && m.cooperative) add_coop_points(m,attacker,mnet); - } - - if(mode.instant_monster_attacks || attacker.is_player) - xy_emit(target,"hit",def,attacker.id); // always sends the event to attacker.id - else - events.push(["hit",def]); - - if(attacker.is_monster) //monster attacks player - { - achievement_logic_monster_hit(attacker,target,attack); - if(target.hp<=0 && !target.rip) - { - if(target.a && target.a.secondchance && Math.random()*1006000) - { - var ms=max(0,-mssince(player.last.attack))+3200,EV=""; - EV="skill_timeout('attack',"+ms+"); "; - player.last.attack=future_ms(ms); - for(var i in player.last) - { - if(G.skills[i] && player.last[i]>future_ms(-3000)) - player.last[i]=future_ms(3200),EV+="skill_timeout('"+i+"',"+(3200+(G.skills[i].cooldown||0))+"); "; + add_call_cost(player, 8, "transport"); // New - to offset send_all_xy [26/01/20] + if (0 && !effect && mssince(player.last.transport) > 6000) { + var ms = max(0, -mssince(player.last.attack)) + 3200; + var EV = ""; + EV = "skill_timeout('attack'," + ms + "); "; + player.last.attack = future_ms(ms); + for (var i in player.last) { + if (G.skills[i] && player.last[i] > future_ms(-3000)) { + player.last[i] = future_ms(3200); + EV += "skill_timeout('" + i + "'," + (3200 + (G.skills[i].cooldown || 0)) + "); "; + } } } - if(effect) player.s.penalty_cd={ms:min(((player.s.penalty_cd&&player.s.penalty_cd.ms)||0)+812,120000)}; - else player.s.penalty_cd={ms:min(((player.s.penalty_cd&&player.s.penalty_cd.ms)||0)+3200,120000)}; - player.socket.emit("new_map",{name:instance.map,in:name,x:player.x,y:player.y,direction:direction,effect:effect||0,info:instance.info,m:player.m,entities:send_all_xy(player,{raw:true}),eval:EV}); - player.last.transport=new Date(); - resend(player,"u+cid"); + if (effect) { + player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 812, 120000) }; + } else { + player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 3200, 120000) }; + } + player.socket.emit("new_map", { + name: instance.map, + in: name, + x: player.x, + y: player.y, + direction: direction, + effect: effect || 0, + info: instance.info, + m: player.m, + entities: send_all_xy(player, { raw: true }), + eval: EV, + }); + player.last.transport = new Date(); + resend(player, "u+cid"); } -function add_shells(player,amount,reason,announce,override) -{ - if(gameplay=="hardcore" || gameplay=="test") return; - var phrase="Received"; - if(reason=="xptome") phrase="Earned"; - player.cash+=amount; - player.socket.emit("game_log",{message:phrase+" "+to_pretty_num(amount)+" SHELLS",color:"green"}); - disappearing_text(player.socket,player,"+"+to_pretty_num(amount),{color:colors.cash,xy:1,s:"cash",size:"huge"}); - appengine_call("bill_user",{auth:player.auth,amount:-parseInt(amount),reason:reason+"_drop",name:player.name,override:override},function(result){ - if(result.failed || !result.done) return; - player.cash=result.cash; - resend(player,"reopen"); +function add_shells(player, amount, reason, announce, override) { + if (gameplay == "hardcore" || gameplay == "test") { + return; + } + var phrase = "Received"; + if (reason == "xptome") { + phrase = "Earned"; + } + player.cash += amount; + player.socket.emit("game_log", { message: phrase + " " + to_pretty_num(amount) + " SHELLS", color: "green" }); + disappearing_text(player.socket, player, "+" + to_pretty_num(amount), { + color: colors.cash, + xy: 1, + s: "cash", + size: "huge", }); - if(announce) broadcast("server_message",{"message":player.name+" found "+to_pretty_num(amount)+" shells","color":"#85C76B","type":"server_found",shells:amount,name:player.name}); + appengine_call( + "bill_user", + { auth: player.auth, amount: -parseInt(amount), reason: reason + "_drop", name: player.name, override: override }, + function (result) { + if (result.failed || !result.done) { + return; + } + player.cash = result.cash; + resend(player, "reopen"); + }, + ); + if (announce) { + broadcast("server_message", { + message: player.name + " found " + to_pretty_num(amount) + " shells", + color: "#85C76B", + type: "server_found", + shells: amount, + name: player.name, + }); + } } -function is_socket_allowed(socket) -{ - var loose=0; - for(var id in sockets) - { - if(!players[id] && get_ip(sockets[id])==get_ip(socket)) loose++; +function is_socket_allowed(socket) { + var loose = 0; + for (var id in sockets) { + if (!players[id] && get_ip(sockets[id]) == get_ip(socket)) { + loose++; + } + } + if (loose > 5) { + return false; } - if(loose>5) return false; return true; } -function disconnect_old_sockets(socket) -{ - for(var id in sockets) - { - if(id!=socket.id && !players[id] && get_ip(sockets[id])==get_ip(socket)) - { - sockets[id].emit("disconnect_reason","Too many loose connections from your network. Simply reload to play."); - if(sockets[id]) sockets[id].disconnect(); // emit can trigger a disconnect too, so this would throw an exception, bring down the server +function disconnect_old_sockets(socket) { + for (var id in sockets) { + if (id != socket.id && !players[id] && get_ip(sockets[id]) == get_ip(socket)) { + sockets[id].emit("disconnect_reason", "Too many loose connections from your network. Simply reload to play."); + if (sockets[id]) { + sockets[id].disconnect(); + } // emit can trigger a disconnect too, so this would throw an exception, bring down the server } } } -function init_io(){ -io.on('connection', function (socket) { - if(socket.handshake.query.server_method) - { - if(0 && socket.handshake.query.server_master==variables.server_master) // this was to make servers communicate with each other and disconnect overflows immediately [28/10/23] - { - if(socket.handshake.query.server_method=="players") - { - socket.emit("players"); // decided to make the existing cron more aggressive [26/09/21] +function init_io() { + io.on("connection", function (socket) { + if (socket.handshake.query.server_method) { + if (0 && socket.handshake.query.server_master == variables.server_master) { + // this was to make servers communicate with each other and disconnect overflows immediately [28/10/23] + if (socket.handshake.query.server_method == "players") { + socket.emit("players"); // decided to make the existing cron more aggressive [26/09/21] + } } + socket.disconnect(); + return; } - socket.disconnect(); - return; - } - sockets[socket.id]=socket; - socket.total_calls=0; - socket.calls=[]; - socket.fs={}; // function list - - if(!is_socket_allowed(socket)) disconnect_old_sockets(socket); + sockets[socket.id] = socket; + socket.total_calls = 0; + socket.calls = []; + socket.fs = {}; // function list - var original_on=socket.on; - socket.on=function(method,f){ - // takes the "f" function, the function thats sent to socket.on, wraps it into a "g" function - var g=function(data){ - ls_method=method; - if(mode.log_all) console.log("'"+method+"': "+JSON.stringify(data)); - try{ - var climit=limits.calls,name="_observer"; - if(data===undefined) data={}; // data normalisation [28/08/18] - socket.total_calls++; add_call_cost(-1); - current_socket=socket; - call_modifier=({"open_chest":0.1,"skill":0.05,"target":0.5}[method])||1; - if(players[socket.id]) - { - name=players[socket.id].name; - // if(players[socket.id].type=="merchant") climit=round(climit/3); - // Merchants are first class citizens now! [14/01/18] + if (!is_socket_allowed(socket)) { + disconnect_old_sockets(socket); + } + + var original_on = socket.on; + socket.on = function (method, f) { + // takes the "f" function, the function thats sent to socket.on, wraps it into a "g" function + var g = function (data) { + ls_method = method; + if (mode.log_all) { + console.log("'" + method + "': " + JSON.stringify(data)); } - else - { - climit=round(climit/4); - } - if(method=="cm") - { - var add=1,len=data.message.length,mult=1; - if(len>100) add=2; - else if(len>1000) add=3; - else if(len>10000) add=10; - else if(len>50000) add=20; - // console.log("add: "+add+"len: "+len); - if(data.to.length>1) mult=0.8; - add_call_cost(add*data.to.length*mult,undefined,"cm_data"); - } - else - { - if(CC[method]) add_call_cost(CC[method]||0); - } - if(get_call_cost()>climit && method!="disconnect") - { - server_log(">>> LIMITDC "+name,1); - socket.emit("limitdcreport",{calls:socket.calls,climit:climit,total:socket.total_calls}); - socket.emit("disconnect_reason","limitdc"); - socket.disconnect(); - } - else f(data); - } - catch(e){ - try{ - var climit=limits.calls; - add_call_cost(16); - log_trace("socket.on: "+method.substr(0,200),e); - if(get_call_cost()>climit) - { - server_log(">>> LIMITDC2 "+name,1); - socket.emit("limitdcreport",{calls:socket.calls,climit:climit,total:socket.total_calls,method:method.substr(0,200)}); - socket.emit("disconnect_reason","limitdc"); - socket.disconnect(); + try { + var climit = limits.calls; + var name = "_observer"; + if (data === undefined) { + data = {}; + } // data normalisation [28/08/18] + socket.total_calls++; + add_call_cost(-1); + current_socket = socket; + call_modifier = { open_chest: 0.1, skill: 0.05, target: 0.5 }[method] || 1; + if (players[socket.id]) { + name = players[socket.id].name; + // if(players[socket.id].type=="merchant") climit=round(climit/3); + // Merchants are first class citizens now! [14/01/18] + } else { + climit = round(climit / 4); } - else - { - try{ - socket.emit("game_error","ERROR!"); - }catch(e){} - } - }catch(e){ - log_trace("limit_calls",e); - } - } - current_socket=false_socket; - } - socket.fs[method]=f; - original_on.apply(socket,[method,g]); - }; - - var data={region:region,name:server_name,pvp:is_pvp,gameplay:gameplay,info:instances[socket.first_map]&&instances[socket.first_map].info||{}}; - socket.first_map=socket.first_in=observer_map; - socket.first_x=observer_x; - socket.first_y=observer_y+120; - socket.desktop=true; - if(socket.request && socket.request._query && socket.request._query.secret) - { - for(var id in players) - { - var player=players[id]; - if(player.secret==socket.request._query.secret) - { - socket.player=player; - data.character=player_to_client(player); data.character.id=data.character.name=player.name; - socket.first_map=player.map; - socket.first_in=player.in; - socket.first_x=player.x; - socket.first_y=player.y; - if(socket.request._query.desktop) socket.desktop=true,socket.first_y+=120; - else socket.desktop=false; - } - } - } - data.x=socket.first_x; data.y=socket.first_y; data.map=socket.first_map; data.in=socket.first_in; - broadcast_e(true); - data.S=E; - socket.emit('welcome',data); - socket.on('send_updates',function(){ - if(observers[socket.id]) send_all_xy(observers[socket.id]); - if(players[socket.id]) send_all_xy(players[socket.id]); - }) - socket.on('loaded',function(data){ - var observer=observers[socket.id]={ - socket:socket, - x:socket.first_x,y:socket.first_y, - // vision:[round((data.width/2)/data.scale)+B.ext_vision,round((data.height/2)/data.scale)+B.ext_vision], - map:socket.first_map, - in:socket.first_in, - observer:1, - id:"o"+socket.id, - s:{}, - }; - if(socket.player) observer.player=socket.player; - // observer.vision[0]=min(1000,observer.vision[0]); observer.vision[1]=min(700,observer.vision[1]); - observer.vision=B.vision; - // socket.emit("observing",{map:observer.map,x:observer.x,y:observer.y}); - resume_instance(instances[observer.in]); - instances[observer.in].observers[observer.id]=observer; - send_all_xy(observer); - }); - socket.on('o:home',function(data){ - var observer=observers[socket.id]; - if(!observer) return; - var player=observer.player; - if(!player || player.dc) return; - transport_observer_to(observer,player.in,player.map,player.x,player.y+(socket.desktop&&120||0)); - }); - socket.on('o:command',function(data){ - var observer=observers[socket.id]; - if(!observer) return; - var player=observer.player; - if(!player || player.dc) return; - player.socket.emit('code_eval',data); - }); - socket.on('cm',function(data){ - var player=players[socket.id],receivers=[]; - if(!player || player.s.mute) return fail_response("muted"); - data.to.forEach(function(name){ - var p=players[name_to_id[name]]; - if(p) p.socket.emit("cm",{name:player.name,message:data.message||""}),receivers.push(name); - }); - success_response("data",{locals:[],receivers:receivers}); - }); - socket.on('say',function(data){ - var player=players[socket.id],message=strip_string(data.message).substr(0,1200); if(!player || player.s.mute) return fail_response("muted"); - if(data.code && player.last_say && ssince(player.last_say)<15) return fail_response("chat_slowdown"); - if(player.last_say && mssince(player.last_say)<400) return fail_response("chat_slowdown"); - if(!message || !message.length) return fail_response("invalid"); - player.last_say=new Date(); - if(data.party) - { - if(!player.party) return fail_response("not_in_a_party"); - party_emit(player.party,"partym",{owner:player.name,message:message,id:player.id,p:true}); - } - else if(data.name) - { - var target=get_player(data.name); - if(!target) - { - player.socket.emit("pm",{owner:player.name,to:data.name,message:message,id:player.id,xserver:true}); - appengine_call("log_chat",{to:["",data.name],type:"xprivate",message:message,fro:player.name,author:player.owner},function(result){ - if(result.failed && players[socket.id]) - player.socket.emit("pm",{owner:player.name,to:data.name,message:"(FAILED)",id:player.id,xserver:true}); - }); - } - else - { - if(target.name==player.name) return fail_response("invalid"); - player.socket.emit("pm",{owner:player.name,to:data.name,message:message,id:player.id}); - target.socket.emit("pm",{owner:player.name,message:message,id:player.id}); - appengine_call("log_chat",{to:[target.owner,target.name],type:"private",message:message,fro:player.name,author:player.owner}); - } - } - else - { - if(1) - { - broadcast("chat_log",{owner:player.name,message:message,id:player.id,p:true}); - var owners={}; - for(var id in players) - { - var p=players[id]; - owners[p.owner]=owners[p.owner]||[]; - owners[p.owner].push(p.name); - } - appengine_call("log_chat",{to:Object.entries(owners),type:"ambient",message:message,fro:player.name,author:player.owner}); - } - else - xy_emit(player,"chat_log",{owner:player.name,message:message,id:player.id,p:true}); - appengine_call("log_chat",{type:"server",message:message,fro:player.name,author:player.owner}); - } - if(player.s.typing) - { - delete player.s.typing; - resend(player,"u+cid+nc"); - } - success_response(); - }); - socket.on('ping_trig',function(data){ socket.emit("ping_ack",data); }); - socket.on('target',function(data){ - var player=players[socket.id]; if(!player) return; - reduce_call_cost(); - player.target=data.id; - player.focus=data.xid; - var target=instances[player.in].monsters[data.id]; - if(!target) target=instances[player.in].players[data.id]; - if(!target) target=instances[player.in].players[NPC_prefix+data.id]; - if(!target) player.target=null; - var focus=instances[player.in].monsters[data.xid]; - if(!focus) focus=instances[player.in].players[data.xid]; - if(!focus) focus=instances[player.in].players[NPC_prefix+data.xid]; - if(!focus) player.focus=null; - if(focus && focus.screenshot) - { - target.going_x=player.x; - target.going_y=player.y; - target.u=true; - start_moving_element(focus); - } - if(focus && player.map=="cgallery" && focus.npc) - { - // target.going_x=player.x; - // target.going_y=player.y; - // target.u=true; - // start_moving_element(target); - if(!player.tcx) player.tcx={}; - if(focus.ctype=="body") - player.tskin=focus.skin; - else if(player.cx.length>5) return fail_response("invalid"); - player.tcx[focus.ctype]=focus.cx[focus.ctype]; - // player.tcx=target.cx; - resend(player,"u+cid"); - } - // server_log(player.name+" target: "+player.target+" focus: "+player.focus); - resend(player,"u+nc"); - success_response({}); - }); - socket.on('ureward',function(data){ - if(!player || player.user || !data.name || !G.docs.rewards[data.name]) return; - if(!player.verified || !player.auth_id) return socket.emit("game_response","reward_notverified"); - if(!player.user.rewards) player.user.rewards=[]; - if(player.user.rewards.includes(data.name)) return socket.emit("game_response","reward_already"); - player.user.rewards.push(data.name); - exchange(player,G.docs.rewards[data.name],{name:"reward_"+data.name}); - socket.emit("game_response",{response:"reward_received",rewards:player.user.rewards}); - - }); - socket.on('creward',function(data){ - if(!player || !data.name || !G.classes[player.type].rewards[data.name]) return; - if(!player.verified || !player.auth_id) return socket.emit("game_response","reward_notverified"); - if(!player.p.rewards) player.p.rewards=[]; - if(player.p.rewards.includes(data.name)) return socket.emit("game_response","reward_already"); - player.p.rewards.push(data.name); - exchange(player,G.classes[player.type].rewards[data.name].reward,{name:"reward_"+data.name}); - socket.emit("game_response",{response:"reward_received",rewards:player.p.rewards}); - - }); - socket.on('cx',function(data){ - var player=players[socket.id]; if(!player) return; - // if(player.role!="gm") return socket.emit('game_log',"Cosmetics system is out of the test phase for now"); - // console.log(data); - var cx=player.cx,cxl=all_cx(player); - // if(!player.tcx) player.tcx=clone(player.cx); - if(data.slot && !data.name) - { - if(data.slot=="back") delete cx.tail; // synced with render_cgallery - if(data.slot=="face") delete cx.makeup; - delete cx[data.slot]; - } - else - { - if(!T[data.name] || !cxl[data.name] && player.role!="cx") return fail_response("cx_not_found"); - if((T[data.name]=="body" || T[data.name]=="armor" || T[data.name]=="character") && data.slot=="skin") player.skin=data.name; - else if((T[data.name]=="body" || T[data.name]=="armor") && data.slot=="upper") cx.upper=data.name; - else if(cxtype_to_slot[T[data.name]] && cxtype_to_slot[T[data.name]]!="skin") - cx[cxtype_to_slot[T[data.name]]]=data.name; - } - prune_cx(player.cx,player.skin); - resend(player,"u+cid"); - success_response(); - }); - socket.on('gm',function(data){ - var player=players[socket.id]; if(!player || player.role!="gm") return; - var target=players[id_to_id[data.id]],action=data.action; - if(action=="mute") - { - if(!target) return socket.emit("game_log","Player not found: "+data.id); - if(!target.s.mute) - { - target.s.mute={ms:48*60*60*1000}; - return socket.emit("game_chat","Muted "+target.name); - } - else - { - target.s.mute={ms:0}; - return socket.emit("game_chat","Unmuted "+target.name); - } - } - else if(action=="jail") - { - if(!target) return socket.emit("game_log","Player not found: "+data.id); - transport_player_to(target,"jail"); - } - else if(action=="ban") - { - appengine_call("ban_user",{name:data.id},function(result){ - socket.emit("game_log","Ban: "+(result&&result.result||"No result")); - }); - } - else if(action=="invincible") - { - player.s.invincible={ms:24*60*60*1000}; - resend(player,"u+cid"); - } - else if(action=="jump") - { - if(!target) return socket.emit("game_log","Player not found: "+data.id); - var spot=safe_xy_nearby(target.map,target.x-8,target.y-6); - if(spot) - { - pulled=player; - pulled.s.magiport={ms:400}; - pulled.s.magiport.x=spot.x; - pulled.s.magiport.y=spot.y; - pulled.s.magiport.f=target.name; - pulled.s.magiport.in=target.in; - pulled.s.magiport.map=target.map; - resend(pulled,"u+cid") - } - else return socket.emit("game_log","No safe spot near: "+data.id); - } - else if(action=="mjump") - { - target=get_monsters(data.monster)[0]; - if(!target) return socket.emit("game_log","Monster not found: "+data.monster); - var spot=safe_xy_nearby(target.map,target.x-8,target.y-6); - if(spot) - { - pulled=player; - pulled.s.magiport={ms:400}; - pulled.s.magiport.x=spot.x; - pulled.s.magiport.y=spot.y; - pulled.s.magiport.f=target.name; - pulled.s.magiport.in=target.in; - pulled.s.magiport.map=target.map; - resend(pulled,"u+cid") - } - else return socket.emit("game_log","No safe spot near: "+data.monster); - } - else if(action=="jump_list") - { - var ids=[]; - for(var id in players) - ids.push(players[id].name); - socket.emit("gm",{action:"jump_list",ids:ids}); - } - else if(action=="server_info") - { - var info=[]; - for(var id in players) - { - info.push({name:players[id].name,owner:players[id].owner,ip:get_ip(players[id])}); - } - //socket.emit("gm",{action:"server_info",info:info}); - } - }); - socket.on('monsterhunt',function(data){ - var player=players[socket.id]; if(!player) return; - if(simple_distance(G.maps.main.ref.monsterhunter,player,true)>B.sell_dist) return fail_response("distance"); - var hunted=[]; - for(var id in server.s) - { - if(server.s[id].type=="monsterhunt") - hunted.push(server.s[id].id); - } - if(player.s.monsterhunt && player.s.monsterhunt.c) return fail_response("monsterhunt_already"); - else if(player.s.monsterhunt) - { - delete server.s["monsterhunt_"+player.s.monsterhunt.id] - delete player.s.monsterhunt; - add_item(player,"monstertoken",{log:true,q:(gameplay=="hardcore"&&100||1)}); - resend(player,"u+cid+reopen"); - return success_response({completed:true}); - } - if(player.type=="merchant") return socket.emit("game_response","monsterhunt_merchant"); - var mmax=-1,name="goo",count=100,times=0,the_hp=0; - for(var id in instances) - { - if(instances[id].name!=id || !G.maps[id] || G.maps[id].irregular) continue; - for(var mid in instances[id].monsters) - { - var monster=instances[id].monsters[mid]; - if(monster.level>mmax && !in_arr(monster.type,hunted) && !monster.target) // added the target condition [21/07/23] - { - name=monster.type; - mmax=monster.level; - the_hp=monster.max_hp/1000.0; - } - } - } - for(var id in G.maps) - { - if(G.maps[id].irregular || !G.maps[id].monsters) continue; - G.maps[id].monsters.forEach(function(p){ - if(p.type==name) times+=p.count; - }); - } - // console.log(times); - count=max(1,min(500,parseInt(20*60*max(1,times)/the_hp/(G.monsters[name].respawn+0.25)))); - if(gameplay=="hardcore") count=max(1,parseInt(count/10)) - player.s.monsterhunt={sn:region+" "+server_name,id:name,c:count,ms:30*60*1000,dl:true}; - server.s["monsterhunt_"+name]={name:player.name,id:name,ms:20*60*1000,type:"monsterhunt"}; - player.hitchhikers.push(["game_response","monsterhunt_started"]); - resend(player,"u+cid"); - success_response({started:true}); - }); - socket.on('ccreport',function(){ - socket.emit('ccreport',{calls:socket.calls,climit:limits.calls,total:socket.total_calls}); - }); - socket.on('tracker',function(data){ - var player=players[socket.id]; if(!player) return; - if(!player.tracker) return; - var data={monsters:player.p.stats.monsters,monsters_diff:player.p.stats.monsters_diff,exchanges:player.p.stats.exchanges,maps:D.drops.maps,tables:{},max:player.max_stats}; // ,computer:false - function register_table(table) - { - if(table) - table.forEach(function(drop){ - if(drop[1]=="open" && !data.tables[drop[2]]) - { - data.tables[drop[2]]=D.drops[drop[2]]; - register_table(D.drops[drop[2]]); - } - }); - } - if(player.computer || 1) - { - // data.computer=true; - data.drops=D.drops.monsters; - } - else - { - data.drops={}; - for(var name in data.monsters) - if(data.monsters[name]>=100 && D.drops.monsters[name]) - data.drops[name]=D.drops.monsters[name]; - } - if(D.drops.maps.global) data.global=D.drops.maps.global,register_table(data.global); - if(D.drops.maps.global_static) data.global_static=D.drops.maps.global_static,register_table(data.global_static); - for(var name in data.drops) - register_table(data.drops[name]); - for(var name in data.maps) - register_table(data.maps[name]); - for(var name in G.items) - if(G.items[name].e && (player.computer || 1 || player.p.stats.exchanges && player.p.stats.exchanges[name] && player.p.stats.exchanges[name]>=100)) - { - if(G.items[name].upgrade || G.items[name].compound) - { - for(var i=0;i<13;i++) - if(D.drops[name+i]) - { - data.tables[name+i]=D.drops[name+i]; - register_table(data.tables[name+i]); + if (method == "cm") { + var add = 1; + var len = data.message.length; + var mult = 1; + if (len > 100) { + add = 2; + } else if (len > 1000) { + add = 3; + } else if (len > 10000) { + add = 10; + } else if (len > 50000) { + add = 20; } + // console.log("add: "+add+"len: "+len); + if (data.to.length > 1) { + mult = 0.8; + } + add_call_cost(add * data.to.length * mult, undefined, "cm_data"); + } else { + if (CC[method]) { + add_call_cost(CC[method] || 0); + } + } + if (get_call_cost() > climit && method != "disconnect") { + server_log(">>> LIMITDC " + name, 1); + socket.emit("limitdcreport", { calls: socket.calls, climit: climit, total: socket.total_calls }); + socket.emit("disconnect_reason", "limitdc"); + socket.disconnect(); + } else { + f(data); + } + } catch (e) { + try { + var climit = limits.calls; + add_call_cost(16); + log_trace("socket.on: " + method.substr(0, 200), e); + if (get_call_cost() > climit) { + server_log(">>> LIMITDC2 " + name, 1); + socket.emit("limitdcreport", { + calls: socket.calls, + climit: climit, + total: socket.total_calls, + method: method.substr(0, 200), + }); + socket.emit("disconnect_reason", "limitdc"); + socket.disconnect(); + } else { + try { + socket.emit("game_error", "ERROR!"); + } catch (e) {} + } + } catch (e) { + log_trace("limit_calls", e); + } } - else if(D.drops[name]) - { - data.tables[name]=D.drops[name]; - register_table(data.tables[name]); + current_socket = false_socket; + }; + socket.fs[method] = f; + original_on.apply(socket, [method, g]); + }; + + var data = { + region: region, + name: server_name, + pvp: is_pvp, + gameplay: gameplay, + info: (instances[socket.first_map] && instances[socket.first_map].info) || {}, + }; + socket.first_map = socket.first_in = observer_map; + socket.first_x = observer_x; + socket.first_y = observer_y + 120; + socket.desktop = true; + if (socket.request && socket.request._query && socket.request._query.secret) { + for (var id in players) { + var player = players[id]; + if (player.secret == socket.request._query.secret) { + socket.player = player; + data.character = player_to_client(player); + data.character.id = data.character.name = player.name; + socket.first_map = player.map; + socket.first_in = player.in; + socket.first_x = player.x; + socket.first_y = player.y; + if (socket.request._query.desktop) { + socket.desktop = true; + socket.first_y += 120; + } else { + socket.desktop = false; + } } } - socket.emit('tracker',data) - }); - socket.on('set_home',function(data){ - var player=players[socket.id]; if(!player) return; - if(player.p.dt.last_homeset && hsince(player.p.dt.last_homeset)<36) return fail_response("sh_time",{hours:36-hsince(player.p.dt.last_homeset)}); - player.p.dt.last_homeset=new Date(); - player.p.home=region+server_name; - delete player.s.hopsickness; - success_response("home_set",{home:player.p.home}); - }); - socket.on('code',function(data){ - var player=players[socket.id]; if(!player) return; - if(data.run) players[socket.id].code=true; - else players[socket.id].code=false; - resend(players[socket.id],"u+cid"); - }); - socket.on('property',function(data){ - var player=players[socket.id]; if(!player) return; - if(data.typing) - { - if(!player.s.typing || player.s.typing.ms<3000) reduce_call_cost(); - player.s.typing={ms:4000}; - resend(player,"u+cid+nc"); } - else - { - var original=player.afk; - if(player.afk=="bot" || player.afk=="code"); - else if(data.afk===true) - { - player.afk=true; + data.x = socket.first_x; + data.y = socket.first_y; + data.map = socket.first_map; + data.in = socket.first_in; + broadcast_e(true); + data.S = E; + socket.emit("welcome", data); + socket.on("send_updates", function () { + if (observers[socket.id]) { + send_all_xy(observers[socket.id]); } - else if(data.afk===false) - { - if(player.afk!==undefined) player.afk=false; + if (players[socket.id]) { + send_all_xy(players[socket.id]); } - if(original!==player.afk) - { - reduce_call_cost(); - resend(player,"u+cid+nc"); - } - } - }); - socket.on('cruise',function(speed){ - var player=players[socket.id]; if(!player) return; - player.cruise=parseInt(speed); - resend(player,"u+cid"); - success_response("cruise",{speed:player.cruise}); - }); - socket.on('test',function(data){ - if(is_sdk) console.log(data.test); - socket.emit('test',{date:new Date()}); - }); - socket.on('blocker',function(data){ - if(data.type=="pvp") - { - if(instances["arena"].allow) - { - socket.emit("blocker",{type:"pvp",allow:1}); - } - else - { - socket.emit("blocker",{type:"pvp"}); - } - } - }); - socket.on('mail_take_item',function(data){ - var player=players[socket.id]; if(!player) return; - if(mode.prevent_external) return socket.emit("game_response",{response:"not_in_this_server"}); - if(!player.esize) return socket.emit("game_response","inv_size"); - appengine_call("take_item_from_mail",{owner:player.owner,mid:data.id},function(result){ - var player=players[socket.id]; - if(result.failed) return socket.emit("game_response",{response:"mail_item_already_taken"}); - var item=JSON.parse(result.item); - add_item(player,item,{announce:false}); - resend(player,"reopen"); - socket.emit("game_response",{response:"mail_item_taken"}); - },function(){ - socket.emit("game_response",{response:"mail_take_item_failed"}); }); - }); - socket.on('mail',function(data){ - var player=players[socket.id]; if(!player) return; - if(mode.prevent_external) return fail_response("not_in_this_server"); - var item=null,retries=1; - if(player.gold<48000) return fail_response("gold_not_enough"); - player.gold-=48000; - if(data.item && player.items[0] && player.items[0].name!="placeholder") - { - if(player.items[0].l) return fail_response("item_locked"); - if(player.items[0].b || player.items[0].v) return fail_response("item_blocked"); - if(player.gold<312000) return fail_response("gold_not_enough"); - player.gold-=312000; - item=JSON.stringify(player.items[0]); - player.items[0]=player.citems[0]=null; - retries=3; - } - appengine_call("send_mail",{fro:player.name,to:data.to,subject:data.subject||"",message:data.message||"",rid:randomStr(50),retries:retries,item:item},function(result){ - var player=players[socket.id]; - if(result.failed) - { - if(player) socket.emit("game_response",{response:"mail_failed",to:data.to,reason:result.reason,cevent:"mail_failed"}); - if(player && item && result.return && player.esize) - { - var r=JSON.parse(item); - add_item(player,r); - resend(player,"reopen"); - } - else - console.log("#M unsent mail, lost item: "+item); + socket.on("loaded", function (data) { + var observer = (observers[socket.id] = { + socket: socket, + x: socket.first_x, + y: socket.first_y, + // vision:[round((data.width/2)/data.scale)+B.ext_vision,round((data.height/2)/data.scale)+B.ext_vision], + map: socket.first_map, + in: socket.first_in, + observer: 1, + id: "o" + socket.id, + s: {}, + }); + if (socket.player) { + observer.player = socket.player; + } + // observer.vision[0]=min(1000,observer.vision[0]); observer.vision[1]=min(700,observer.vision[1]); + observer.vision = B.vision; + // socket.emit("observing",{map:observer.map,x:observer.x,y:observer.y}); + resume_instance(instances[observer.in]); + instances[observer.in].observers[observer.id] = observer; + send_all_xy(observer); + }); + socket.on("o:home", function (data) { + var observer = observers[socket.id]; + if (!observer) { return; } - if(player) socket.emit("game_response",{response:"mail_sent",to:data.to,cevent:"mail_sent"}); - },function(){ - var player=players[socket.id]; - if(player) socket.emit("game_response",{response:"mail_failed",reason:"coms_failure"}); - if(item) console.log("#M unsent mail, lost item: "+item); + var player = observer.player; + if (!player || player.dc) { + return; + } + transport_observer_to(observer, player.in, player.map, player.x, player.y + ((socket.desktop && 120) || 0)); }); - resend(player,"reopen"); - success_response("mail_sending",{sucess:false,in_progress:true,received:"unknown"}); - }); - socket.on('leave',function(data){ - var player=players[socket.id]; if(!player) return; - if(!can_walk(player)) return fail_response("transport_failed"); - if(0 && player.s.block || player.targets>5 || !(player.map=="jail" || player.map=="cyberland" || instances[player.in].solo)) - return fail_response("cant_escape"); - transport_player_to(player,B.start_map); - success_response(); - }); - socket.on('transport',function(data){ - var player=players[socket.id],can_reach=false; if(!player) return; // this was missing, probably caused a production exception - should be added everywhere [04/09/16] - if(!can_walk(player) || player.map=="jail") return fail_response("transport_failed"); - var new_map=G.maps[data.to],s=data.s||0,the_door=null; - if(!new_map || !instances[data.to] || !instances[data.to].allow) return fail_response("cant_enter"); - if(0 && player.s.block || player.targets>5) return fail_response("cant_escape"); - - (G.maps[player.map].doors||[]).forEach(function(door){ - if(!can_reach && door[4]==data.to && s==(door[5]||0) && simple_distance({map:player.map,x:G.maps[player.map].spawns[door[6]][0],y:G.maps[player.map].spawns[door[6]][1]},player)5) return fail_response("cant_escape"); - // if(player.role!="gm" && !(data.place==player.map && player.map=="cgallery")) - // { - // if(data.place!="resort" && !G.maps[player.map].ref.transporter || simple_distance(G.maps[player.map].ref.transporter,player)>80) return socket.emit("game_response","transport_cant_reach"); - // if(data.place=="resort" && player.map!="resort") return socket.emit("game_response","transport_cant_reach"); - // } - server_log(data); - var name=randomStr(24); - if(data.place=="resort" && 0) - { - var name="resort_"+data.name; - instance=instances[name]||create_instance(name,"resort_map"); - transport_player_to(player,name); - } - else if(data.place=="duelland") - { - if(instances[data.name] && instances[data.name].map=="duelland") - { - transport_player_to(player,data.name); - instance_emit(data.name,"game_log",{message:player.name+" is spectating the duel",color:"gray"}); - } - else return fail_response("cant_enter"); - } - else if(data.place=="crypt" || data.place=="winter_instance") - { - var f="cave",ref=G.maps.cave.spawns[2],item="cryptkey"; - if(data.place=="winter_instance") f="winterland",ref=G.maps.winterland.spawns[5],item="frozenkey"; - if(simple_distance(player,{in:f,map:f,x:ref[0],y:ref[1]})>120) - return fail_response("transport_cant_reach"); - if(data.name && instances[data.name] && instances[data.name].map==data.place) - { - transport_player_to(player,data.name); - } - else - { - if(!consume_one_by_id(player,item)) - return fail_response("transport_cant_item"); - instance=create_instance(name,data.place); - transport_player_to(player,name); - } - resend(player,"u+cid+reopen"); - } - else if(data.place=="dungeon0" && player.role=="gm") - { - instance=create_instance(name,"dungeon0",{solo:player.id}); - transport_player_to(player,name); - } - else if(data.place=="cgallery" && player.role=="gm") - { - var point=null; - if(player.map=="cgallery") point=[player.x,player.y]; - instance=create_instance(name,"cgallery",{solo:player.id}); - transport_player_to(player,name,point); - var bodies=[],heads=[],hairs=[],wings=[],hats=[]; - for(var s in G.sprites) - { - var current=G.sprites[s],matrix=current.matrix; - if(current.skip || current.rskip) continue; - if(!in_arr(current.type,["body","head","hair","s_wings","hat"])) continue; - for(var i=0;i5) return fail_response("cant_escape"); - player.c.town={ms:min(player.c.town&&player.c.town.ms||5000,3000)}; - resend(player,"u+nc"); - success_response({success:false,in_progress:true}) - }); - socket.on('respawn',function(data){ - var player=players[socket.id]; if(!player || !player.rip) return fail_response("invalid"); - if(player.rip_time && ssince(player.rip_time) 5) { + return fail_response("invalid"); + } + player.tcx[focus.ctype] = focus.cx[focus.ctype]; + // player.tcx=target.cx; + resend(player, "u+cid"); + } + // server_log(player.name+" target: "+player.target+" focus: "+player.focus); + resend(player, "u+nc"); + success_response({}); + }); + socket.on("ureward", function (data) { + if (!player || player.user || !data.name || !G.docs.rewards[data.name]) { + return; + } + if (!player.verified || !player.auth_id) { + return socket.emit("game_response", "reward_notverified"); + } + if (!player.user.rewards) { + player.user.rewards = []; + } + if (player.user.rewards.includes(data.name)) { + return socket.emit("game_response", "reward_already"); + } + player.user.rewards.push(data.name); + exchange(player, G.docs.rewards[data.name], { name: "reward_" + data.name }); + socket.emit("game_response", { response: "reward_received", rewards: player.user.rewards }); + }); + socket.on("creward", function (data) { + if (!player || !data.name || !G.classes[player.type].rewards[data.name]) { + return; + } + if (!player.verified || !player.auth_id) { + return socket.emit("game_response", "reward_notverified"); + } + if (!player.p.rewards) { + player.p.rewards = []; + } + if (player.p.rewards.includes(data.name)) { + return socket.emit("game_response", "reward_already"); + } + player.p.rewards.push(data.name); + exchange(player, G.classes[player.type].rewards[data.name].reward, { name: "reward_" + data.name }); + socket.emit("game_response", { response: "reward_received", rewards: player.p.rewards }); + }); + socket.on("cx", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + // if(player.role!="gm") return socket.emit('game_log',"Cosmetics system is out of the test phase for now"); + // console.log(data); + var cx = player.cx; + var cxl = all_cx(player); + // if(!player.tcx) player.tcx=clone(player.cx); + if (data.slot && !data.name) { + if (data.slot == "back") { + delete cx.tail; + } // synced with render_cgallery + if (data.slot == "face") { + delete cx.makeup; + } + delete cx[data.slot]; + } else { + if (!T[data.name] || (!cxl[data.name] && player.role != "cx")) { + return fail_response("cx_not_found"); + } + if ((T[data.name] == "body" || T[data.name] == "armor" || T[data.name] == "character") && data.slot == "skin") { + player.skin = data.name; + } else if ((T[data.name] == "body" || T[data.name] == "armor") && data.slot == "upper") { + cx.upper = data.name; + } else if (cxtype_to_slot[T[data.name]] && cxtype_to_slot[T[data.name]] != "skin") { + cx[cxtype_to_slot[T[data.name]]] = data.name; + } + } + prune_cx(player.cx, player.skin); + resend(player, "u+cid"); + success_response(); + }); + socket.on("gm", function (data) { + var player = players[socket.id]; + if (!player || player.role != "gm") { + return; + } + var target = players[id_to_id[data.id]]; + var action = data.action; + if (action == "mute") { + if (!target) { + return socket.emit("game_log", "Player not found: " + data.id); + } + if (!target.s.mute) { + target.s.mute = { ms: 48 * 60 * 60 * 1000 }; + return socket.emit("game_chat", "Muted " + target.name); + } else { + target.s.mute = { ms: 0 }; + return socket.emit("game_chat", "Unmuted " + target.name); + } + } else if (action == "jail") { + if (!target) { + return socket.emit("game_log", "Player not found: " + data.id); + } + transport_player_to(target, "jail"); + } else if (action == "ban") { + appengine_call("ban_user", { name: data.id }, function (result) { + socket.emit("game_log", "Ban: " + ((result && result.result) || "No result")); + }); + } else if (action == "invincible") { + player.s.invincible = { ms: 24 * 60 * 60 * 1000 }; + resend(player, "u+cid"); + } else if (action == "jump") { + if (!target) { + return socket.emit("game_log", "Player not found: " + data.id); + } + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (spot) { + pulled = player; + pulled.s.magiport = { ms: 400 }; + pulled.s.magiport.x = spot.x; + pulled.s.magiport.y = spot.y; + pulled.s.magiport.f = target.name; + pulled.s.magiport.in = target.in; + pulled.s.magiport.map = target.map; + resend(pulled, "u+cid"); + } else { + return socket.emit("game_log", "No safe spot near: " + data.id); + } + } else if (action == "mjump") { + target = get_monsters(data.monster)[0]; + if (!target) { + return socket.emit("game_log", "Monster not found: " + data.monster); + } + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (spot) { + pulled = player; + pulled.s.magiport = { ms: 400 }; + pulled.s.magiport.x = spot.x; + pulled.s.magiport.y = spot.y; + pulled.s.magiport.f = target.name; + pulled.s.magiport.in = target.in; + pulled.s.magiport.map = target.map; + resend(pulled, "u+cid"); + } else { + return socket.emit("game_log", "No safe spot near: " + data.monster); + } + } else if (action == "jump_list") { + var ids = []; + for (var id in players) { + ids.push(players[id].name); + } + socket.emit("gm", { action: "jump_list", ids: ids }); + } else if (action == "server_info") { + var info = []; + for (var id in players) { + info.push({ name: players[id].name, owner: players[id].owner, ip: get_ip(players[id]) }); + } + //socket.emit("gm",{action:"server_info",info:info}); + } + }); + socket.on("monsterhunt", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (simple_distance(G.maps.main.ref.monsterhunter, player, true) > B.sell_dist) { + return fail_response("distance"); + } + var hunted = []; + for (var id in server.s) { + if (server.s[id].type == "monsterhunt") { + hunted.push(server.s[id].id); + } + } + if (player.s.monsterhunt && player.s.monsterhunt.c) { + return fail_response("monsterhunt_already"); + } else if (player.s.monsterhunt) { + delete server.s["monsterhunt_" + player.s.monsterhunt.id]; + delete player.s.monsterhunt; + add_item(player, "monstertoken", { log: true, q: (gameplay == "hardcore" && 100) || 1 }); + resend(player, "u+cid+reopen"); + return success_response({ completed: true }); + } + if (player.type == "merchant") { + return socket.emit("game_response", "monsterhunt_merchant"); + } + var mmax = -1; + var name = "goo"; + var count = 100; + var times = 0; + var the_hp = 0; + for (var id in instances) { + if (instances[id].name != id || !G.maps[id] || G.maps[id].irregular) { + continue; + } + for (var mid in instances[id].monsters) { + var monster = instances[id].monsters[mid]; + if (monster.level > mmax && !in_arr(monster.type, hunted) && !monster.target) { + // added the target condition [21/07/23] + name = monster.type; + mmax = monster.level; + the_hp = monster.max_hp / 1000.0; + } + } + } + for (var id in G.maps) { + if (G.maps[id].irregular || !G.maps[id].monsters) { + continue; + } + G.maps[id].monsters.forEach(function (p) { + if (p.type == name) { + times += p.count; + } + }); + } + // console.log(times); + count = max(1, min(500, parseInt((20 * 60 * max(1, times)) / the_hp / (G.monsters[name].respawn + 0.25)))); + if (gameplay == "hardcore") { + count = max(1, parseInt(count / 10)); + } + player.s.monsterhunt = { sn: region + " " + server_name, id: name, c: count, ms: 30 * 60 * 1000, dl: true }; + server.s["monsterhunt_" + name] = { name: player.name, id: name, ms: 20 * 60 * 1000, type: "monsterhunt" }; + player.hitchhikers.push(["game_response", "monsterhunt_started"]); + resend(player, "u+cid"); + success_response({ started: true }); + }); + socket.on("ccreport", function () { + socket.emit("ccreport", { calls: socket.calls, climit: limits.calls, total: socket.total_calls }); + }); + socket.on("tracker", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!player.tracker) { + return; + } + var data = { + monsters: player.p.stats.monsters, + monsters_diff: player.p.stats.monsters_diff, + exchanges: player.p.stats.exchanges, + maps: D.drops.maps, + tables: {}, + max: player.max_stats, + }; // ,computer:false + function register_table(table) { + if (table) { + table.forEach(function (drop) { + if (drop[1] == "open" && !data.tables[drop[2]]) { + data.tables[drop[2]] = D.drops[drop[2]]; + register_table(D.drops[drop[2]]); + } + }); + } + } + if (player.computer || 1) { + // data.computer=true; + data.drops = D.drops.monsters; + } else { + data.drops = {}; + for (var name in data.monsters) { + if (data.monsters[name] >= 100 && D.drops.monsters[name]) { + data.drops[name] = D.drops.monsters[name]; + } + } + } + if (D.drops.maps.global) { + data.global = D.drops.maps.global; + register_table(data.global); + } + if (D.drops.maps.global_static) { + data.global_static = D.drops.maps.global_static; + register_table(data.global_static); + } + for (var name in data.drops) { + register_table(data.drops[name]); + } + for (var name in data.maps) { + register_table(data.maps[name]); + } + for (var name in G.items) { + if ( + G.items[name].e && + (player.computer || + 1 || + (player.p.stats.exchanges && player.p.stats.exchanges[name] && player.p.stats.exchanges[name] >= 100)) + ) { + if (G.items[name].upgrade || G.items[name].compound) { + for (var i = 0; i < 13; i++) { + if (D.drops[name + i]) { + data.tables[name + i] = D.drops[name + i]; + register_table(data.tables[name + i]); + } + } + } else if (D.drops[name]) { + data.tables[name] = D.drops[name]; + register_table(data.tables[name]); + } + } + } + socket.emit("tracker", data); + }); + socket.on("set_home", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (player.p.dt.last_homeset && hsince(player.p.dt.last_homeset) < 36) { + return fail_response("sh_time", { hours: 36 - hsince(player.p.dt.last_homeset) }); + } + player.p.dt.last_homeset = new Date(); + player.p.home = region + server_name; + delete player.s.hopsickness; + success_response("home_set", { home: player.p.home }); + }); + socket.on("code", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.run) { + players[socket.id].code = true; + } else { + players[socket.id].code = false; + } + resend(players[socket.id], "u+cid"); + }); + socket.on("property", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.typing) { + if (!player.s.typing || player.s.typing.ms < 3000) { + reduce_call_cost(); + } + player.s.typing = { ms: 4000 }; + resend(player, "u+cid+nc"); + } else { + var original = player.afk; + if (player.afk == "bot" || player.afk == "code") { + } else if (data.afk === true) { + player.afk = true; + } else if (data.afk === false) { + if (player.afk !== undefined) { + player.afk = false; + } + } + if (original !== player.afk) { + reduce_call_cost(); + resend(player, "u+cid+nc"); + } + } + }); + socket.on("cruise", function (speed) { + var player = players[socket.id]; + if (!player) { + return; + } + player.cruise = parseInt(speed); + resend(player, "u+cid"); + success_response("cruise", { speed: player.cruise }); + }); + socket.on("test", function (data) { + if (is_sdk) { + console.log(data.test); + } + socket.emit("test", { date: new Date() }); + }); + socket.on("blocker", function (data) { + if (data.type == "pvp") { + if (instances["arena"].allow) { + socket.emit("blocker", { type: "pvp", allow: 1 }); + } else { + socket.emit("blocker", { type: "pvp" }); + } + } + }); + socket.on("mail_take_item", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (mode.prevent_external) { + return socket.emit("game_response", { response: "not_in_this_server" }); + } + if (!player.esize) { + return socket.emit("game_response", "inv_size"); + } + appengine_call( + "take_item_from_mail", + { owner: player.owner, mid: data.id }, + function (result) { + var player = players[socket.id]; + if (result.failed) { + return socket.emit("game_response", { response: "mail_item_already_taken" }); + } + var item = JSON.parse(result.item); + add_item(player, item, { announce: false }); + resend(player, "reopen"); + socket.emit("game_response", { response: "mail_item_taken" }); + }, + function () { + socket.emit("game_response", { response: "mail_take_item_failed" }); + }, + ); + }); + socket.on("mail", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (mode.prevent_external) { + return fail_response("not_in_this_server"); + } + var item = null; + var retries = 1; + if (player.gold < 48000) { + return fail_response("gold_not_enough"); + } + player.gold -= 48000; + if (data.item && player.items[0] && player.items[0].name != "placeholder") { + if (player.items[0].l) { + return fail_response("item_locked"); + } + if (player.items[0].b || player.items[0].v) { + return fail_response("item_blocked"); + } + if (player.gold < 312000) { + return fail_response("gold_not_enough"); + } + player.gold -= 312000; + item = JSON.stringify(player.items[0]); + player.items[0] = player.citems[0] = null; + retries = 3; + } + appengine_call( + "send_mail", + { + fro: player.name, + to: data.to, + subject: data.subject || "", + message: data.message || "", + rid: randomStr(50), + retries: retries, + item: item, + }, + function (result) { + var player = players[socket.id]; + if (result.failed) { + if (player) { + socket.emit("game_response", { + response: "mail_failed", + to: data.to, + reason: result.reason, + cevent: "mail_failed", + }); + } + if (player && item && result.return && player.esize) { + var r = JSON.parse(item); + add_item(player, r); + resend(player, "reopen"); + } else { + console.log("#M unsent mail, lost item: " + item); + } + return; + } + if (player) { + socket.emit("game_response", { response: "mail_sent", to: data.to, cevent: "mail_sent" }); + } + }, + function () { + var player = players[socket.id]; + if (player) { + socket.emit("game_response", { response: "mail_failed", reason: "coms_failure" }); + } + if (item) { + console.log("#M unsent mail, lost item: " + item); + } + }, + ); + resend(player, "reopen"); + success_response("mail_sending", { sucess: false, in_progress: true, received: "unknown" }); + }); + socket.on("leave", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!can_walk(player)) { + return fail_response("transport_failed"); + } + if ( + (0 && player.s.block) || + player.targets > 5 || + !(player.map == "jail" || player.map == "cyberland" || instances[player.in].solo) + ) { + return fail_response("cant_escape"); + } + transport_player_to(player, B.start_map); + success_response(); + }); + socket.on("transport", function (data) { + var player = players[socket.id]; + var can_reach = false; + if (!player) { + return; + } // this was missing, probably caused a production exception - should be added everywhere [04/09/16] + if (!can_walk(player) || player.map == "jail") { + return fail_response("transport_failed"); + } + var new_map = G.maps[data.to]; + var s = data.s || 0; + var the_door = null; + if (!new_map || !instances[data.to] || !instances[data.to].allow) { + return fail_response("cant_enter"); + } + if ((0 && player.s.block) || player.targets > 5) { + return fail_response("cant_escape"); + } + + (G.maps[player.map].doors || []).forEach(function (door) { + if ( + !can_reach && + door[4] == data.to && + s == (door[5] || 0) && + simple_distance( + { map: player.map, x: G.maps[player.map].spawns[door[6]][0], y: G.maps[player.map].spawns[door[6]][1] }, + player, + ) < B.door_dist + ) { + can_reach = "door"; + the_door = door; + } + }); + if ((player.map == "woffice" && gameplay == "hardcore") || player.role == "gm") { + can_reach = "transport"; + } + if ( + !can_reach && + G.maps[player.map].ref.transporter && + simple_distance(G.maps[player.map].ref.transporter, player) < B.transporter_dist && + G.npcs.transporter.places[data.to] === s + ) { + can_reach = "transport"; + } + if (can_reach == "transport" && player.s.dampened) { + return fail_response("transport_cant_dampened"); + } + if (!can_reach) { + return fail_response("transport_cant_reach"); + } + if (the_door && the_door[7] == "protected") { + var protected = false; + for (var x in instances[player.in].monsters || {}) { + if (instances[player.in].monsters[x].map_def.gatekeeper) { + protected = true; + } + } + if (protected) { + return fail_response("transport_cant_protection"); + } + } + if ( + the_door && + the_door[7] == "ulocked" && + G.maps[data.to].mount && + player.user && + !player.user.unlocked[data.to] + ) { + return fail_response("transport_cant_locked"); + } + + if (instances[data.to].mount && !player.user) { + if (player.mounting || player.unmounting) { + return fail_response("bank_opi"); + } + add_call_cost(32, undefined, "bank"); + player.mounting = new Date(); + player.mount_to = data.to; + player.mount_s = s; + sync_loop(); + return success_response({ success: false, in_progress: true }); + } else if (!instances[data.to].mount && player.user) { + // previously handled at transport_player_to + if (player.mounting || player.unmounting) { + return fail_response("bank_opi"); + } + add_call_cost(16, undefined, "bank"); + player.unmounting = new Date(); + player.unmount_to = data.to; + player.unmount_s = s; + sync_loop(); + return success_response({ success: false, in_progress: true }); + } else if (0 && instances[data.to].mount && player.user && data.to != player.map) { + // an easy prevention for the bank re-entry nuisance, bank can be re-entered physically but not digitally [03/11/16] + // commented out for bank_u / bank_b [06/05/20] + return fail_response("transport_failed"); + } else { + decay_s(player, 5200); + transport_player_to(player, data.to, s); + return success_response(); + } + }); + socket.on("enter", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (gameplay != "normal") { + return fail_response("transport_failed"); + } + if (!can_walk(player) || player.map == "jail") { + return fail_response("transport_failed"); + } + if (player.s.block || player.targets > 5) { + return fail_response("cant_escape"); + } + // if(player.role!="gm" && !(data.place==player.map && player.map=="cgallery")) + // { + // if(data.place!="resort" && !G.maps[player.map].ref.transporter || simple_distance(G.maps[player.map].ref.transporter,player)>80) return socket.emit("game_response","transport_cant_reach"); + // if(data.place=="resort" && player.map!="resort") return socket.emit("game_response","transport_cant_reach"); + // } + server_log(data); + var name = randomStr(24); + if (data.place == "resort" && 0) { + var name = "resort_" + data.name; + instance = instances[name] || create_instance(name, "resort_map"); + transport_player_to(player, name); + } else if (data.place == "duelland") { + if (instances[data.name] && instances[data.name].map == "duelland") { + transport_player_to(player, data.name); + instance_emit(data.name, "game_log", { message: player.name + " is spectating the duel", color: "gray" }); + } else { + return fail_response("cant_enter"); + } + } else if (data.place == "crypt" || data.place == "winter_instance") { + var f = "cave"; + var ref = G.maps.cave.spawns[2]; + var item = "cryptkey"; + if (data.place == "winter_instance") { + f = "winterland"; + ref = G.maps.winterland.spawns[5]; + item = "frozenkey"; + } + if (simple_distance(player, { in: f, map: f, x: ref[0], y: ref[1] }) > 120) { + return fail_response("transport_cant_reach"); + } + if (data.name && instances[data.name] && instances[data.name].map == data.place) { + transport_player_to(player, data.name); + } else { + if (!consume_one_by_id(player, item)) { + return fail_response("transport_cant_item"); + } + instance = create_instance(name, data.place); + transport_player_to(player, name); + } + resend(player, "u+cid+reopen"); + } else if (data.place == "dungeon0" && player.role == "gm") { + instance = create_instance(name, "dungeon0", { solo: player.id }); + transport_player_to(player, name); + } else if (data.place == "cgallery" && player.role == "gm") { + var point = null; + if (player.map == "cgallery") { + point = [player.x, player.y]; + } + instance = create_instance(name, "cgallery", { solo: player.id }); + transport_player_to(player, name, point); + var bodies = []; + var heads = []; + var hairs = []; + var wings = []; + var hats = []; + for (var s in G.sprites) { + var current = G.sprites[s]; + var matrix = current.matrix; + if (current.skip || current.rskip) { + continue; + } + if (!in_arr(current.type, ["body", "head", "hair", "s_wings", "hat"])) { + continue; + } + for (var i = 0; i < matrix.length; i++) { + for (var j = 0; j < matrix[i].length; j++) { + if (!matrix[i][j]) { + continue; + } + if (current.type == "body") { + bodies.push(matrix[i][j]); + } + if (current.type == "head") { + heads.push(matrix[i][j]); + } + if (current.type == "hair") { + hairs.push(matrix[i][j]); + } + if (current.type == "hat") { + hats.push(matrix[i][j]); + } + if (current.type == "s_wings") { + wings.push(matrix[i][j]); + } + T[matrix[i][j]] = current.type; + } + } + } + var xs = [-80, -40, 0, 40, 80]; + for (var n = 0; n < bodies.length; n++) { + var npc = create_npc( + { + name: bodies[n], + level: 1, + speed: 12, + hp: 1000, + skin: bodies[n], + cx: {}, + }, + { + position: [xs[n % xs.length], -parseInt(n / xs.length) * 40], + id: bodies[n], + }, + instance, + ); + npc.ctype = "body"; + npc.cx = { head: heads[n] }; + npc.id = "body: " + bodies[n]; + instance.players[NPC_prefix + npc.id] = npc; + } + for (var n = 0; n < heads.length; n++) { + var npc = create_npc( + { name: heads[n], level: 1, speed: 12, hp: 1000, skin: bodies[0], cx: {} }, + { position: [xs[n % xs.length] + 240, -parseInt(n / xs.length) * 40], id: heads[n] }, + instance, + ); + npc.ctype = "head"; + npc.cx = { head: heads[n] }; + npc.id = "head: " + heads[n]; + instance.players[NPC_prefix + npc.id] = npc; + } + for (var n = 0; n < hairs.length; n++) { + var npc = create_npc( + { name: hairs[n], level: 1, speed: 12, hp: 1000, skin: bodies[1], cx: {} }, + { position: [xs[n % xs.length] + 480, -parseInt(n / xs.length) * 40], id: hairs[n] }, + instance, + ); + npc.ctype = "hair"; + npc.cx = { head: heads[0], hair: hairs[n] }; + npc.id = "hair: " + hairs[n]; + instance.players[NPC_prefix + npc.id] = npc; + } + for (var n = 0; n < hats.length; n++) { + var npc = create_npc( + { name: hats[n], level: 1, speed: 12, hp: 1000, skin: bodies[2], cx: {} }, + { position: [xs[n % xs.length] + 720, -parseInt(n / xs.length) * 40], id: hats[n] }, + instance, + ); + npc.ctype = "hat"; + npc.cx = { head: heads[0], hat: hats[n] }; + npc.id = "hair: " + hats[n]; + instance.players[NPC_prefix + npc.id] = npc; + } + } else { + return fail_response("transport_cant_reach"); + } + success_response(); + }); + socket.on("town", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + // if(player.last.town && mssince(player.last.town)<1200) return; // bad ui experience [Unknown] - got reported and disabled [25/03/22] + if (!can_walk(player) || player.map == "jail") { + return fail_response("transport_failed"); + } + if ((0 && player.s.block) || player.targets > 5) { + return fail_response("cant_escape"); + } + player.c.town = { ms: min((player.c.town && player.c.town.ms) || 5000, 3000) }; + resend(player, "u+nc"); + success_response({ success: false, in_progress: true }); + }); + socket.on("respawn", function (data) { + var player = players[socket.id]; + if (!player || !player.rip) { + return fail_response("invalid"); + } + if (player.rip_time && ssince(player.rip_time) < B.rip_time) { + return fail_response("cant_respawn"); + } + + delete player.s.block; + player.hp = player.max_hp; + player.mp = round(player.max_mp / 2); + player.rip = false; + if (gameplay == "hardcore") { + reset_player(player, 1); + } + + if (data && data.safe) { + // gameplay=="hardcore" && + transport_player_to(player, "woffice", 0, 1); + } else { + var place = G.maps[player.map].on_death || G.maps[B.start_map].on_death || ["main", 0]; + transport_player_to(player, place[0], place[1], 1); + } + if (player.party) { + send_party_update(player.party); + } + invincible_logic(player); + resend(player, "u+cid"); + success_response("data", { place: "respawn", cevent: "respawn" }); + }); + socket.on("random_look", function (data) { + return socket.emit("game_log", "socket.emit('enter',{place:'cgallery'})"); + var player = players[socket.id]; + if (!player) { + return; + } + var bodies = []; + var heads = []; + var hairs = []; + var wings = []; + var hats = []; + if (player.rlooks == 25 && !is_sdk && player.role != "gm") { + return; + } + if (player.role == "gm") { + reduce_call_cost(40); + } + player.rlooks = (player.rlooks || 0) + 1; + for (var s in G.sprites) { + var current = G.sprites[s]; + var matrix = current.matrix; + if (current.skip || current.rskip) { + continue; + } + if (!in_arr(current.type, ["body", "head", "hair", "s_wings", "hat"])) { + continue; + } + for (var i = 0; i < matrix.length; i++) { + for (var j = 0; j < matrix[i].length; j++) { + if (!matrix[i][j]) { + continue; + } + if (current.type == "body") { + bodies.push(matrix[i][j]); + } + if (current.type == "head") { + heads.push(matrix[i][j]); + } + if (current.type == "hair") { + hairs.push(matrix[i][j]); + } + if (current.type == "hat") { + hats.push(matrix[i][j]); + } + if (current.type == "s_wings") { + wings.push(matrix[i][j]); + } + } + } + } + var head = "head"; + if (Math.random() < 0.1) { + head = random_one(heads); + } + player.tskin = random_one(bodies); + player.tcx = [head, random_one(hairs)]; + if (Math.random() < 0.08) { + player.tcx.push(random_one(wings)); + } + if (Math.random() < 0.4) { + player.tcx.push(random_one(hats)); + } + resend(player, "u+cid"); + }); + socket.on("unlock", function (data) { + return; // [27/06/18] + var player = players[socket.id]; + if (!player) { + return; + } + if (gameplay == "normal") { + return; + } // ? [27/06/18] + if (data.name == "code") { + var item = player.items[data.num || 0]; + if (!item || item.name != "computer" || item.charges === 0 || item.charges < 0 || player.unlocking_code) { return; } - socket.emit("game_log","CODE slots increased to 100!"); - resend(player,"reopen"); + if (!item.charges) { + item.charges = 2; + } + item.charges--; + player.citems[data.num || 0] = player.items[data.num || 0]; // explicitly linked to the actual item + player.unlocking_code = true; + appengine_call( + "user_operation", + { auth: player.auth, operation: "code_unlock", suffix: "/code_unlock/" + player.owner, retries: 4 }, + function (result) { + server_log("user_operation_code: " + JSON.stringify(result), 1); + if (result.failed) { + if (result.reason == "already") { + socket.emit( + "game_log", + "Your CODE slots are already unlocked. You can lend your Ancient Computer to a friend in need, that's why there are 2 charges :]", + ); + item.charges++; + } else { + socket.emit("game_log", "Unlock Failed. Email hello@adventure.land with a screenshot."); + } + return; + } + socket.emit("game_log", "CODE slots increased to 100!"); + resend(player, "reopen"); + }, + ); + } + }); + socket.on("dismantle", function (data) { + var player = players[socket.id]; + var check = true; + var items = []; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!player.computer && simple_distance(G.maps.main.ref.craftsman, player) > B.sell_dist) { + return fail_response("distance"); + } + // if(player.esize<=0) return socket.emit("game_response","inventory_full"); + var item = player.items[data.num]; + if (item && item.level && G.items[item.name].compound) { + var cost = min(50000000, calculate_item_value(item) * 10); + if (player.gold < cost) { + return fail_response("gold_not_enough"); + } + if (player.esize < 2) { + return fail_response("inv_size"); + } + if (G.items[item.name].type == "booster") { + return fail_response("dismantle_cant"); + } + player.gold -= cost; + consume_one(player, data.num); + add_item( + player, + { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, + { announce: false }, + ); + add_item( + player, + { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, + { announce: false }, + ); + add_item( + player, + { name: item.name, level: item.level - 1, grace: parseInt((item.grace || 0) / 3) }, + { announce: false }, + ); + resend(player, "reopen+nc+inv"); + return success_response("dismantle", { name: item.name, level: item.level, cost: cost, cevent: true }); + } + if (!item || !G.dismantle[item.name]) { + return fail_response("dismantle_cant"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + if (player.gold < G.dismantle[item.name].cost) { + return fail_response("gold_not_enough"); + } + if ( + !can_add_items(player, list_to_pseudo_items(G.dismantle[item.name].items), { + space: ((item.q || 1) == 1 && 1) || 0, + }) + ) { + return fail_response("inv_size"); + } + player.gold -= G.dismantle[item.name].cost; + consume_one(player, data.num); + G.dismantle[item.name].items.forEach(function (e) { + if (e[0] < 1 && Math.random() > e[0]) { + return; + } + add_item(player, e[1], { q: max(1, e[0]), p: item.p && !G.titles[item.p].misc && item.p }); }); - } - }); - socket.on('dismantle',function(data){ - var player=players[socket.id],check=true,items=[]; - if(!player || player.user) return fail_response("cant_in_bank"); - if(!player.computer && simple_distance(G.maps.main.ref.craftsman,player)>B.sell_dist) return fail_response("distance"); - // if(player.esize<=0) return socket.emit("game_response","inventory_full"); - var item=player.items[data.num]; - if(item && item.level && G.items[item.name].compound) - { - var cost=min(50000000,calculate_item_value(item)*10); - if(player.golde[0]) return; - add_item(player,e[1],{q:max(1,e[0]),p:item.p&&!G.titles[item.p].misc&&item.p}); + resend(player, "reopen+nc+inv"); + success_response("dismantle", { name: item.name, cevent: true }); }); - resend(player,"reopen+nc+inv"); - success_response("dismantle",{name:item.name,cevent:true}); - }); - socket.on('craft',function(data){ - var player=players[socket.id],check=true,items=[],quantity={},place={},space=false,p={},locked=false; - if(!player || player.user) return fail_response("cant_in_bank"); - data.items.forEach(function(x){ - if(!player.items[x[1]]) check=false; - else if(player.items[x[1]].l || player.items[x[1]].b) check=false,locked=true; - else - { - var name=player.items[x[1]].name; - if(player.items[x[1]].level) name+="+"+player.items[x[1]].level; - items.push(name); - quantity[player.items[x[1]].name]=(player.items[x[1]].q||1); // (quantity[player.items[x[1]].name]||0)+ removed this part, seems like a bad initial addition [17/06/18] - place[player.items[x[1]].name]=x[1]; - if(player.items[x[1]].p && !G.titles[player.items[x[1]].p].misc) p[player.items[x[1]].p]=player.items[x[1]].p; + socket.on("craft", function (data) { + var player = players[socket.id]; + var check = true; + var items = []; + var quantity = {}; + var place = {}; + var space = false; + var p = {}; + var locked = false; + if (!player || player.user) { + return fail_response("cant_in_bank"); } + data.items.forEach(function (x) { + if (!player.items[x[1]]) { + check = false; + } else if (player.items[x[1]].l || player.items[x[1]].b) { + check = false; + locked = true; + } else { + var name = player.items[x[1]].name; + if (player.items[x[1]].level) { + name += "+" + player.items[x[1]].level; + } + items.push(name); + quantity[player.items[x[1]].name] = player.items[x[1]].q || 1; // (quantity[player.items[x[1]].name]||0)+ removed this part, seems like a bad initial addition [17/06/18] + place[player.items[x[1]].name] = x[1]; + if (player.items[x[1]].p && !G.titles[player.items[x[1]].p].misc) { + p[player.items[x[1]].p] = player.items[x[1]].p; + } + } + }); + if (locked) { + return fail_response("item_locked"); + } + if (!check) { + return fail_response("no_item"); + } + //if(items.length<2) return socket.emit("game_response","craft_atleast2"); + items.sort(); + var key = items.join(","); + // console.log(key); + if (!D.craftmap[key]) { + return fail_response("craft_cant"); + } + var name = D.craftmap[key]; + var enough = true; + if ( + !player.computer && + !G.craft[name].quest && + simple_distance(get_npc_coords("craftsman"), player) > B.sell_dist + ) { + return fail_response("distance"); + } + if ( + !player.computer && + G.craft[name].quest && + simple_distance(get_npc_coords(G.craft[name].quest), player) > B.sell_dist + ) { + return fail_response("distance"); + } + if (player.gold < G.craft[name].cost) { + return fail_response("gold_not_enough"); + } + G.craft[name].items.forEach(function (i) { + if (quantity[i[1]] < i[0]) { + enough = false; + } + if (quantity[i[1]] == i[0]) { + space = true; + } + }); + if (!space && !can_add_item(player, create_new_item(name))) { + return fail_response("inventory_full"); + } + if (!enough) { + return fail_response("craft_cant_quantity"); + } + player.gold -= G.craft[name].cost; + G.craft[name].items.forEach(function (x) { + consume(player, place[x[1]], x[0]); + }); + var i = add_item(player, name, { r: 1, p: Object.keys(p).length && random_one(p) }); + resend(player, "reopen+nc+inv"); + success_response("craft", { num: i, name: name, cevent: true }); }); - if(locked) return fail_response("item_locked") - if(!check) return fail_response("no_item"); - //if(items.length<2) return socket.emit("game_response","craft_atleast2"); - items.sort(); - var key=items.join(","); - // console.log(key); - if(!D.craftmap[key]) return fail_response("craft_cant"); - var name=D.craftmap[key],enough=true; - if(!player.computer && !G.craft[name].quest && simple_distance(get_npc_coords('craftsman'),player)>B.sell_dist) return fail_response("distance"); - if(!player.computer && G.craft[name].quest && simple_distance(get_npc_coords(G.craft[name].quest),player)>B.sell_dist) return fail_response("distance"); - if(player.gold B.sell_dist) { + return fail_response("distance"); + } + if (!player.computer && def.quest && simple_distance(G.quests[def.quest], player) > B.sell_dist) { + return fail_response("distance"); + } + if (player.esize <= 0 && !((item.q || 1) == 1)) { + return fail_response("inventory_full"); + } + if (def.e > 1 && item.q < def.e) { + return fail_response("exchange_notenough"); + } + player.p.stats.exchanges[item.name + suffix] = (player.p.stats.exchanges[item.name + suffix] || 0) + 1; + consume(player, data.item_num, def.e); + var num = add_item(player, "placeholder"); + var ms = 3000 + parseInt(Math.random() * 3000); + if (gameplay == "hardcore") { + ms = 400; + } + player.q.exchange = { ms: ms, len: ms, name: item.name, id: item.name + suffix, q: def.e, num: num }; + if (suffix) { + player.q.exchange.s = suffix; + } + if (item.v) { + player.q.exchange.v = item.v; + } + if (def.quest) { + player.q.exchange.qs = def.quest; + } + resend(player, "reopen+nc+inv"); + success_response({ success: false, in_progress: true, num: num }); }); - if(!space && !can_add_item(player,create_new_item(name))) return fail_response("inventory_full"); - if(!enough) return fail_response("craft_cant_quantity"); - player.gold-=G.craft[name].cost; - G.craft[name].items.forEach(function(x){ - consume(player,place[x[1]],x[0]); - }); - var i=add_item(player,name,{r:1,p:Object.keys(p).length&&random_one(p)}); - resend(player,"reopen+nc+inv"); - success_response("craft",{num:i,name:name,cevent:true}) - }); - socket.on('exchange',function(data){ - var player=players[socket.id],item=player.items[data.item_num],def=G.items[item&&item.name],suffix=""; - if(player.q.exchange) return fail_response("exchange_existing"); - if(def && (def.compound||def.upgrade)) suffix=item.level||0; - if(!player || player.user) return fail_response("cant_in_bank"); - G.maps.main.exchange.name=player.name; - if(!def || !def.e || item.q!=data.q || !D.drops[item.name+suffix]) return fail_response("invalid"); - if(item.l) return fail_response("item_locked"); - if(!player.computer && !def.quest && simple_distance(G.maps.main.exchange,player)>B.sell_dist) return fail_response("distance"); - if(!player.computer && def.quest && simple_distance(G.quests[def.quest],player)>B.sell_dist) return fail_response("distance"); - if(player.esize<=0 && !((item.q||1)==1)) return fail_response("inventory_full"); - if(def.e>1 && item.qB.sell_dist) return fail_response("distance"); - if(item.q B.sell_dist) { + return fail_response("distance"); + } + if (item.q < G.tokens[item.name][data.name]) { + return fail_response("exchange_notenough"); + } - if(G.tokens[item.name][data.name]<1) - { - var q=parseInt(1/G.tokens[item.name][data.name]); - if(item.q!=1 && !can_add_item(player,{name:data.name,q:q})) return fail_response("inventory_full"); - consume(player,data.num,1); - num=add_item(player,data.name,{q:q,announce:false,r:true}); - } - else - { - var name=data.name,idata=undefined; - if(name.search("-")!=-1) { idata=name.split("-")[1]; name=name.split("-")[0]; } - var new_item=create_new_item(name); - if(idata) new_item.data=idata; - if(item.q!=G.tokens[item.name][data.name] && !can_add_item(player,new_item)) return fail_response("inventory_full"); - consume(player,data.num,G.tokens[item.name][data.name]); - num=add_item(player,new_item,{announce:false,r:true}); - } - - xy_emit(npc,"upgrade",{type:item.name+"s",success:1}); - resend(player,"reopen+nc+inv"); - success_response({num:num,cevent:true}); - }); - socket.on('locksmith',function(data){ - var player=players[socket.id],item=player.items[data.item_num],def=G.items[item&&item.name]; - if(!player || player.user) return fail_response("cant_in_bank"); - if(!player.computer && simple_distance(G.maps.desertland.ref.locksmith,player)>B.sell_dist) return fail_response("distance"); - var item=player.items[data.num]; - if(!item) return fail_response("no_item"); - var def=G.items[item.name]; - if(in_arr(def.type,["uscroll","cscroll","pscroll","offering","tome"])) return fail_response("locksmith_cant"); - if(data.operation=="unlock") - { - if(!item.l) { resend(player,"reopen+nc"); return fail_response("locksmith_aunlocked","locksmith","already_unlocked"); } - if(item.l=="s") - { - if(player.gold<250000) return fail_response("gold_not_enough"); - player.gold-=250000; - item.ld=JSON.stringify(future_s(2*24*60*60)); - item.l="u"; - socket.emit("game_response","locksmith_unsealed"); + if (G.tokens[item.name][data.name] < 1) { + var q = parseInt(1 / G.tokens[item.name][data.name]); + if (item.q != 1 && !can_add_item(player, { name: data.name, q: q })) { + return fail_response("inventory_full"); + } + consume(player, data.num, 1); + num = add_item(player, data.name, { q: q, announce: false, r: true }); + } else { + var name = data.name; + var idata = undefined; + if (name.search("-") != -1) { + idata = name.split("-")[1]; + name = name.split("-")[0]; + } + var new_item = create_new_item(name); + if (idata) { + new_item.data = idata; + } + if (item.q != G.tokens[item.name][data.name] && !can_add_item(player, new_item)) { + return fail_response("inventory_full"); + } + consume(player, data.num, G.tokens[item.name][data.name]); + num = add_item(player, new_item, { announce: false, r: true }); } - else if(item.l=="u") - { - var date=new Date(JSON.parse(item.ld)); - if(date B.sell_dist) { + return fail_response("distance"); + } + var item = player.items[data.num]; + if (!item) { + return fail_response("no_item"); + } + var def = G.items[item.name]; + if (in_arr(def.type, ["uscroll", "cscroll", "pscroll", "offering", "tome"])) { + return fail_response("locksmith_cant"); + } + if (data.operation == "unlock") { + if (!item.l) { + resend(player, "reopen+nc"); + return fail_response("locksmith_aunlocked", "locksmith", "already_unlocked"); + } + if (item.l == "s") { + if (player.gold < 250000) { + return fail_response("gold_not_enough"); + } + player.gold -= 250000; + item.ld = JSON.stringify(future_s(2 * 24 * 60 * 60)); + item.l = "u"; + socket.emit("game_response", "locksmith_unsealed"); + } else if (item.l == "u") { + var date = new Date(JSON.parse(item.ld)); + if (date < new Date()) { + delete item.l; + socket.emit("game_response", "locksmith_unseal_complete"); + } else { + resend(player, "reopen+nc"); + return success_response("locksmith_unsealing", { hours: -hsince(date), success: false, in_progress: true }); + } + } else { + if (player.gold < 250000) { + return fail_response("gold_not_enough"); + } + player.gold -= 250000; delete item.l; - socket.emit("game_response","locksmith_unseal_complete"); + socket.emit("game_response", "locksmith_unlocked"); } - else - { - resend(player,"reopen+nc"); - return success_response("locksmith_unsealing",{hours:-hsince(date),success:false,in_progress:true}); + } else if (data.operation == "lock") { + if (item.l) { + resend(player, "reopen+nc"); + return fail_response("locksmith_alocked", "locksmith", "already_locked"); } + if (player.gold < 250000) { + return fail_response("gold_not_enough"); + } + player.gold -= 250000; + item.l = "l"; + socket.emit("game_response", "locksmith_locked"); + } else if (data.operation == "seal") { + if (player.gold < 250000) { + return fail_response("gold_not_enough"); + } + player.gold -= 250000; + item.l = "s"; + socket.emit("game_response", "locksmith_sealed"); } - else - { - if(player.gold<250000) return fail_response("gold_not_enough"); - player.gold-=250000; - delete item.l; - socket.emit("game_response","locksmith_unlocked"); - } - } - else if(data.operation=="lock") - { - if(item.l) { resend(player,"reopen+nc"); return fail_response("locksmith_alocked","locksmith","already_locked"); } - if(player.gold<250000) return fail_response("gold_not_enough"); - player.gold-=250000; - item.l="l"; - socket.emit("game_response","locksmith_locked"); - } - else if(data.operation=="seal") - { - if(player.gold<250000) return fail_response("gold_not_enough"); - player.gold-=250000; - item.l="s"; - socket.emit("game_response","locksmith_sealed"); - } - player.citems[data.num]=cache_item(player.items[data.num]); - resend(player,"reopen+nc"); - success_response(); - }); - socket.on('compound',function(data){ - try{ - var player=players[socket.id],item0=player.items[data.items[0]],item1=player.items[data.items[1]],item2=player.items[data.items[2]],scroll=player.items[data.scroll_num],result,ex=""; - if(!player || player.user) return fail_response("cant_in_bank"); - G.maps.main.compound.name=player.name; - if(player.q.compound) return fail_response("compound_in_progress","compound","in_progress"); - var offering=player.items[data.offering_num]; - if(offering && G.items[offering.name].type!="offering") return socket.emit("game_response","compound_invalid_offering"); - if(!item0 || (item0.level||0)!=data.clevel) return fail_response("no_item"); - if(!player.computer && simple_distance(G.maps.main.compound,player)>B.sell_dist) return socket.emit("game_response",{response:"distance",place:"compound",failed:true}); - var def=G.items[item0.name],scroll_def=G.items[scroll.name],offering_def=offering&&G.items[offering.name],grade=calculate_item_grade(def,item0); - if(grade==4) return socket.emit("game_response",{response:"max_level",level:item.level,place:"compound",failed:true}); - if(!(item0.name == item1.name && item1.name==item2.name && (item0.level||0)==(item1.level||0) && (item1.level||0)==(item2.level||0) )) return socket.emit("game_response","compound_mismatch"); - if(!def.compound) return socket.emit("game_response","compound_cant"); - if(scroll_def.type!="cscroll" || grade>scroll_def.grade) return socket.emit("game_response","compound_incompatible_scroll"); - if(data.items[0]==data.items[1] || data.items[1]==data.items[2] || data.items[0]==data.items[2]) return socket.emit("game_response",{response:"misc_fail",place:"compound",failed:true}); - if(item0.l || item1.l || item2.l) return socket.emit("game_response",{response:"item_locked",place:"compound",failed:true}); - - if(!data.calculate) consume_one(player,data.scroll_num); - - var new_level=(item0.level||0)+1,probability=1,oprobability=1,result=0,proc=0,grace=0,igrade=def.igrade,high=false,grace_bonus=0; - if(item0.level>=3) igrade=calculate_item_grade(def,{name:item0.name,level:item0.level-2}); - - delete player.p.c_item; delete player.p.c_itemx; delete player.p.c_roll; - - oprobability=probability=D.compounds[igrade][new_level]; - result=Math.random(); - server_log(result+" < "+probability); - - if(scroll_def.grade > grade) - { - probability=probability*1.1+0.001; - server_log("higher grade scroll "+result+" < "+probability); - if(!data.calculate) grace_bonus+=0.4; - high=scroll_def.grade-grade; - } - - if(offering) - { - var increase=0.5; - if(!data.calculate) consume_one(player,data.offering_num); - grace=0.027*((item0.grace||0)+(item1.grace||0)+(item2.grace||0)+0.5+player.p.ograce); - - if(offering_def.grade>grade+1) - probability=probability*1.64+grace*2,high=true,increase=3; - else if(offering_def.grade>grade) - probability=probability*1.48+grace,high=true,increase=1; - else if(offering_def.grade==grade) - probability=probability*1.36+min(30*0.027,grace); - else if(offering_def.grade==grade-1) - probability=probability*1.15+min(25*0.019,grace)/max(item0.level-2,1),increase=0.2; - else - probability=probability*1.08+min(15*0.015,grace)/max(item0.level-1,1),increase=0.1; - - if(!data.calculate) item0.grace=(item0.grace||0)+(item1.grace||0)+(item2.grace||0),grace_bonus+=increase; - server_log("offering "+result+" < "+probability); - } - else - { - grace=0.007*((item0.grace||0)+(item1.grace||0)+(item2.grace||0)+player.p.ograce); - probability=probability+min(25*0.007,grace)/max(item0.level-1,1); - if(!data.calculate) item0.grace=max(max((item0.grace||0),(item1.grace||0)),(item2.grace||0)); - } - - if(!data.calculate) item0.grace=item0.grace/6.4+grace_bonus; - - if(def.type=="booster") probability=0.9999999999999,proc=offering&&0.12; - else probability=min(probability,min(oprobability*(3+(high&&high*0.6||0)),oprobability+0.2+(high&&high*0.05||0))); - - if(gameplay=="test") result=0; - - server_log(result+" < "+probability+" grace: "+grace); - - if(data.calculate) return success_response("compound_chance",{calculate:true,chance:probability,item:cache_item(item0),scroll:scroll.name,offering:offering&&offering.name||undefined,grace:(item0.grace||0)+(item1.grace||0)+(item2.grace||0)}); - - player.p.c_roll=result; - var len=10000; - if(gameplay=="hardcore") len=1200; - if(player.s.massproduction) { len/=2; delete player.s.massproduction; ex="+u+cid"; } - if(player.s.massproductionpp) { len/=10; delete player.s.massproductionpp; ex="+u+cid"; } - player.q.compound={ms:len,len:len,num:data.items[0],nums:[]}; - player.items[data.items[0]]={"name":"placeholder",p:{chance:probability,name:item0.name,level:item0.level,scroll:scroll.name,offering:offering&&offering.name,nums:[]}}; - player.items[data.items[1]]=null; - player.items[data.items[2]]=null; - - if(result<=probability) - { - if(offering) player.p.ograce=0; - else player.p.ograce*=1-new_level*0.02; - item0.level=new_level; - if((item0.p||item1.p||item2.p)!="legacy") item0.p=(item0.p||item1.p||item2.p); - else { delete item0.p; } - player.p.c_item=item0; - if(item0.oo!=player.name) item0.o=player.name; - player.esize+=2; - if(parseInt(result*10000)==parseInt(probability*10000)) // && grade>=1 - add_achievement(player,"lucky"),add_item_property(item0,"lucky"); - if(item1.v||item2.v) item0.v=item1.v||item2.v; - if(item0.name=="ctristone" && item0.level==1 && Math.random()<0.012) item0.name="cdarktristone"; - if(def.type=="booster") - { - var activate=false; item0.extra=0; - while(offering && proc && Math.random() B.sell_dist) { + return socket.emit("game_response", { response: "distance", place: "compound", failed: true }); + } + var def = G.items[item0.name]; + var scroll_def = G.items[scroll.name]; + var offering_def = offering && G.items[offering.name]; + var grade = calculate_item_grade(def, item0); + if (grade == 4) { + return socket.emit("game_response", { + response: "max_level", + level: item.level, + place: "compound", + failed: true, }); - if(activate) item0.expires=future_s(seconds/3); } - } - else - { - if(offering) player.p.ograce+=0.4; - player.esize+=2; - player.p.c_item=null; - player.p.c_itemx=item0; - } + if ( + !( + item0.name == item1.name && + item1.name == item2.name && + (item0.level || 0) == (item1.level || 0) && + (item1.level || 0) == (item2.level || 0) + ) + ) { + return socket.emit("game_response", "compound_mismatch"); + } + if (!def.compound) { + return socket.emit("game_response", "compound_cant"); + } + if (scroll_def.type != "cscroll" || grade > scroll_def.grade) { + return socket.emit("game_response", "compound_incompatible_scroll"); + } + if (data.items[0] == data.items[1] || data.items[1] == data.items[2] || data.items[0] == data.items[2]) { + return socket.emit("game_response", { response: "misc_fail", place: "compound", failed: true }); + } + if (item0.l || item1.l || item2.l) { + return socket.emit("game_response", { response: "item_locked", place: "compound", failed: true }); + } - player.citems[data.items[0]]=cache_item(player.items[data.items[0]]); - player.citems[data.items[1]]=cache_item(player.items[data.items[1]]); - player.citems[data.items[2]]=cache_item(player.items[data.items[2]]); - - resend(player,"reopen+nc+inv"+ex); + if (!data.calculate) { + consume_one(player, data.scroll_num); + } - }catch(e){ - server_log('compound_e '+e); - return socket.emit("game_response",{response:"exception",place:"compound",failed:true}); - } - }); - socket.on('upgrade',function(data){ - try{ - var player=players[socket.id],item=player.items[data.item_num],scroll=player.items[data.scroll_num],offering=player.items[data.offering_num],result,ex=""; - if(!player || player.user) return fail_response("cant_in_bank"); - if(player.q.upgrade) return socket.emit("game_response","upgrade_in_progress"); - G.maps.main.upgrade.name=player.name; - if(!player.computer && simple_distance(G.maps.main.upgrade,player)>B.sell_dist) return socket.emit("game_response",{response:"distance",place:"upgrade",failed:true}); - if(!item) return socket.emit("game_response","upgrade_no_item"); - if(offering && !(G.items[offering.name] && G.items[offering.name].type=="offering" || G.items[offering.name] && G.items[offering.name].offering!==undefined && !(item.level>0))) return socket.emit("game_response","upgrade_invalid_offering"); - if(!scroll && !offering) return socket.emit("game_response","upgrade_no_scroll"); - if((item.level||0)!=data.clevel) return socket.emit("game_response","upgrade_mismatch"); - var item_def=G.items[item.name],scroll_def=scroll&&G.items[scroll.name],offering_def=offering&&G.items[offering.name],grade=calculate_item_grade(item_def,item); - if(!item_def.upgrade) return socket.emit("game_response","upgrade_cant"); - if(scroll && (!in_arr(scroll_def.type,["uscroll","pscroll"]) || (scroll_def.type=="uscroll" && !item_def.upgrade) || (scroll_def.type=="pscroll" && !item_def.stat) || grade>scroll_def.grade)) - { - if(grade==4 && scroll_def.type=="uscroll") return socket.emit("game_response",{response:"max_level",level:item.level,place:"upgrade",failed:true}); - return socket.emit("game_response","upgrade_incompatible_scroll"); - } + var new_level = (item0.level || 0) + 1; + var probability = 1; + var oprobability = 1; + var result = 0; + var proc = 0; + var grace = 0; + var igrade = def.igrade; + var high = false; + var grace_bonus = 0; + if (item0.level >= 3) { + igrade = calculate_item_grade(def, { name: item0.name, level: item0.level - 2 }); + } - var new_level=(item.level||0)+1,probability=1,oprobability=1,grace=0,high=false; - var ograde=calculate_item_grade(item_def,{name:item.name,level:0}),tmult=1; - if(ograde==1) tmult=1.5; else if(ograde==2) tmult=2; + delete player.p.c_item; + delete player.p.c_itemx; + delete player.p.c_roll; - delete player.p.u_item; delete player.p.u_type; delete player.p.u_itemx; delete player.p.u_roll; delete player.p.u_fail; delete player.p.u_level; - - player.p.u_level=(item.level||0); - - if(!scroll) - { - if(G.items[offering.name] && G.items[offering.name].offering!==undefined) - { - var chance=0.16,ms=2000; - if(G.items[offering.name].offering>ograde || G.items[offering.name].offering==2 && calculate_item_value(item)<=20000000) chance=0.32; - chance*=[2.8,1.6,1][ograde]; - if(G.items[offering.name].offering grade) { + probability = probability * 1.1 + 0.001; + server_log("higher grade scroll " + result + " < " + probability); + if (!data.calculate) { + grace_bonus += 0.4; } - else - { - player.p.u_item=item; - player.p.u_fail=true; + high = scroll_def.grade - grade; + } + + if (offering) { + var increase = 0.5; + if (!data.calculate) { + consume_one(player, data.offering_num); + } + grace = 0.027 * ((item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0) + 0.5 + player.p.ograce); + + if (offering_def.grade > grade + 1) { + probability = probability * 1.64 + grace * 2; + high = true; + increase = 3; + } else if (offering_def.grade > grade) { + probability = probability * 1.48 + grace; + high = true; + increase = 1; + } else if (offering_def.grade == grade) { + probability = probability * 1.36 + min(30 * 0.027, grace); + } else if (offering_def.grade == grade - 1) { + probability = probability * 1.15 + min(25 * 0.019, grace) / max(item0.level - 2, 1); + increase = 0.2; + } else { + probability = probability * 1.08 + min(15 * 0.015, grace) / max(item0.level - 1, 1); + increase = 0.1; + } + + if (!data.calculate) { + item0.grace = (item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0); + grace_bonus += increase; + } + server_log("offering " + result + " < " + probability); + } else { + grace = 0.007 * ((item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0) + player.p.ograce); + probability = probability + min(25 * 0.007, grace) / max(item0.level - 1, 1); + if (!data.calculate) { + item0.grace = max(max(item0.grace || 0, item1.grace || 0), item2.grace || 0); } } - else - { - var ms=1000; - if(data.calculate) return success_response("upgrade_chance",{calculate:true,chance:1,offering:offering.name,item:cache_item(item),grace:item.grace||0}); - consume_one(player,data.offering_num); - item.grace=(item.grace||0)+0.5; - server_log("item.grace: "+item.grace); - player.p.u_type="offering"; - player.p.u_item=item; - player.p.u_roll=0.999999999999999; - if(player.s.massproduction) { ms/=2; delete player.s.massproduction; ex="+u+cid"; } - if(player.s.massproductionpp) { ms/=10; delete player.s.massproductionpp; ex="+u+cid"; } - player.q.upgrade={ms:ms,len:ms,num:data.item_num}; - player.items[data.item_num]={"name":"placeholder",p:{chance:1,name:item.name,level:item.level,scroll:null,offering:offering.name,nums:[]}}; - } - } - else if(scroll_def.type=="uscroll") - { - if(item.l) return socket.emit("game_response",{response:"item_locked",place:"upgrade",failed:true}); - if(grade==4) return socket.emit("game_response",{response:"max_level",level:item.level,place:"upgrade",failed:true}); - if(!data.calculate) consume_one(player,data.scroll_num); - oprobability=probability=D.upgrades[item_def.igrade][new_level]; - // grace=max(0,min(new_level+1, (item.grace||0) + min(3,player.p.ugrace[new_level]/3.0) +min(2,ugrace[new_level]/4.0) +item_def.igrace + player.p.ograce/2.0 )); - original [16/07/18] - grace=max(0,min(new_level+1, (item.grace||0) + min(3,player.p.ugrace[new_level]/4.5) +item_def.igrace) + min(6,S.ugrace[new_level]/3.0) + player.p.ograce/3.2); - server_log("Grace num: "+grace+"\nItem: "+(item.grace||0)+"\nPlayer: "+min(3,player.p.ugrace[new_level]/4.5)+"\nDef: "+item_def.igrace+"\nOgrace:"+(player.p.ograce/3.2)+"\nS.ugrace: "+min(6,S.ugrace[new_level]/3.0)); - grace=(probability*grace/new_level)+grace/1000.0; - server_log("Grace-prob: "+grace); - result=Math.random(); - server_log(result+" < "+probability); - // if(!data.calculate && item.name=="throwingstars" && scroll_def.grade==2 && item.level==4) item.p="superfast"; - - if(scroll_def.grade>grade && new_level<=10) - { - probability=probability*1.2+0.01; - high=true; - if(!data.calculate) item.grace=(item.grace||0)+0.4; + if (!data.calculate) { + item0.grace = item0.grace / 6.4 + grace_bonus; } - if(offering) - { - var increase=0.4; - if(!data.calculate) consume_one(player,data.offering_num); - - if(offering_def.grade>grade+1) - probability=probability*1.7+grace*4,high=true,increase=3; - else if(offering_def.grade>grade) - probability=probability*1.5+grace*1.2,high=true,increase=1; - else if(offering_def.grade==grade) - probability=probability*1.4+grace; - else if(offering_def.grade==grade-1) - probability=probability*1.15+grace/3.2,increase=0.2; - else - probability=probability*1.08+grace/4,increase=0.1; - - if(!data.calculate) item.grace=(item.grace||0)+increase; // previously +1 [16/07/18] - } - else - { - grace=max(0,grace/4.8-0.4/((new_level-0.999)*(new_level-0.999))); - probability+=grace; // previously 12.0 // previously 9.0 [16/07/18] + if (def.type == "booster") { + probability = 0.9999999999999; + proc = offering && 0.12; + } else { + probability = min( + probability, + min(oprobability * (3 + ((high && high * 0.6) || 0)), oprobability + 0.2 + ((high && high * 0.05) || 0)), + ); } - if(!data.calculate && Math.random()<0.025) // Bonus grace - item.grace=(item.grace||0)+1; - - if(data.item_num==player.p.item_num && Math.random()<0.6) // Added [29/10/17] - { - server_log("16 cheat"); - result=max(Math.random()/10000.0,result*0.975-0.012); + if (gameplay == "test") { + result = 0; } - if(high) probability=min(probability,min(oprobability+0.36,oprobability*3)); - else probability=min(probability,min(oprobability+0.24,oprobability*2)); - server_log(result+" < "+probability+" grace: "+grace); + server_log(result + " < " + probability + " grace: " + grace); - if(data.calculate) return success_response("upgrade_chance",{calculate:true,chance:probability,offering:offering&&offering.name||undefined,item:cache_item(item),grace:item.grace||0,scroll:scroll.name}); + if (data.calculate) { + return success_response("compound_chance", { + calculate: true, + chance: probability, + item: cache_item(item0), + scroll: scroll.name, + offering: (offering && offering.name) || undefined, + grace: (item0.grace || 0) + (item1.grace || 0) + (item2.grace || 0), + }); + } - if(gameplay=="test") result=0; - // result=probability; - player.p.u_type="normal"; - player.p.u_roll=result; - var ms=500*new_level*Math.sqrt(new_level)*tmult; - if(player.s.massproduction) { ms/=2; delete player.s.massproduction; ex="+u+cid"; } - if(player.s.massproductionpp) { ms/=10; delete player.s.massproductionpp; ex="+u+cid"; } - player.q.upgrade={ms:ms,len:ms,num:data.item_num}; - if(gameplay=="hardcore") player.q.upgrade.ms=player.q.upgrade.len=500; - player.items[data.item_num]={"name":"placeholder",p:{chance:probability,name:item.name,level:item.level,scroll:scroll.name,offering:offering&&offering.name,nums:[]}}; + player.p.c_roll = result; + var len = 10000; + if (gameplay == "hardcore") { + len = 1200; + } + if (player.s.massproduction) { + len /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + len /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.compound = { ms: len, len: len, num: data.items[0], nums: [] }; + player.items[data.items[0]] = { + name: "placeholder", + p: { + chance: probability, + name: item0.name, + level: item0.level, + scroll: scroll.name, + offering: offering && offering.name, + nums: [], + }, + }; + player.items[data.items[1]] = null; + player.items[data.items[2]] = null; - //result=probability+EPS; - - if(result<=probability) - { - // console.log("here"); - player.p.ugrace[new_level]=S.ugrace[new_level]=0; - if(offering) player.p.ograce*=0.25; - else player.p.ograce*=1-new_level*0.005; - item.level=new_level; - if(item.oo!=player.name) item.o=player.name; - player.p.u_item=item; - if(parseInt(result*10000)==parseInt(probability*10000) && grade>=1) - { - add_item_property(item,"lucky"); - add_achievement(player,"lucky") + if (result <= probability) { + if (offering) { + player.p.ograce = 0; + } else { + player.p.ograce *= 1 - new_level * 0.02; } - } - else - { - player.p.ugrace[new_level-1]+=1; S.ugrace[new_level-1]+=1 - player.p.ugrace[new_level]+=1; S.ugrace[new_level]+=1; - if(new_level>=8 && new_level<=15) - { - player.p.ugrace[new_level-1]+=1; S.ugrace[new_level-1]+=1; - player.p.ugrace[new_level-2]+=2+(offering&&1||0); S.ugrace[new_level-2]+=2; - player.p.ugrace[new_level-3]+=2+(offering&&2||0); S.ugrace[new_level-3]+=3+(offering&&1||0); + item0.level = new_level; + if ((item0.p || item1.p || item2.p) != "legacy") { + item0.p = item0.p || item1.p || item2.p; + } else { + delete item0.p; } - if(offering) player.p.ograce+=0.6; // previously 1 [16/07/18] - if(scroll_def.grade!=3.6) player.p.u_itemx=item; - else player.p.u_item=item,player.p.u_fail=true; - if(parseInt(result*10000)==parseInt(probability*10000)) - { - if(player.esize && grade>=1) add_item(player,"essenceofgreed"); - add_achievement(player,"unlucky"); + player.p.c_item = item0; + if (item0.oo != player.name) { + item0.o = player.name; } - } - } - else if(scroll_def.type=="pscroll") - { - var needed=[1,10,100,1000,9999,9999,9999]; - if(scroll.q=1 + add_achievement(player, "lucky"); + add_item_property(item0, "lucky"); + } + if (item1.v || item2.v) { + item0.v = item1.v || item2.v; + } + if (item0.name == "ctristone" && item0.level == 1 && Math.random() < 0.012) { + item0.name = "cdarktristone"; + } + if (def.type == "booster") { + var activate = false; + item0.extra = 0; + while (offering && proc && Math.random() < proc) { + item0.extra++; + item0.level += 1; + proc /= 2.0; + } + var seconds = 6 * 24 * 60 * 60; + [item0, item1, item2].forEach(function (i) { + if (i.expires) { + seconds -= ssince(i.expires); + activate = true; + } else { + seconds += def.days * 24 * 60 * 60; + } + }); + if (activate) { + item0.expires = future_s(seconds / 3); + } + } + } else { + if (offering) { + player.p.ograce += 0.4; + } + player.esize += 2; + player.p.c_item = null; + player.p.c_itemx = item0; } - item.stat_type=scroll_def.stat; - player.p.u_roll=Math.random(); + player.citems[data.items[0]] = cache_item(player.items[data.items[0]]); + player.citems[data.items[1]] = cache_item(player.items[data.items[1]]); + player.citems[data.items[2]] = cache_item(player.items[data.items[2]]); - player.p.u_type="stat"; - if(player.p.u_roll<=0.99999 || offering) player.p.u_item=item; - else player.p.u_itemx=item; - var ms=2000*tmult*tmult; - if(player.s.massproduction) { ms/=2; delete player.s.massproduction; ex="+u+cid"; } - if(player.s.massproductionpp) { ms/=10; delete player.s.massproductionpp; ex="+u+cid"; } - player.q.upgrade={ms:ms,len:ms,num:data.item_num}; - player.items[data.item_num]={"name":"placeholder",p:{chance:0.99999,name:item.name,level:item.level,scroll:scroll.name,offering:offering&&offering.name,nums:[]}}; + resend(player, "reopen+nc+inv" + ex); + } catch (e) { + server_log("compound_e " + e); + return socket.emit("game_response", { response: "exception", place: "compound", failed: true }); } - - player.citems[data.item_num]=cache_item(player.items[data.item_num]); - - resend(player,"reopen+nc+inv"+ex); - - }catch(e){ - server_log('upgrade_e '+e); - return socket.emit("game_response",{response:"exception",place:"upgrade",failed:true}); - } - }); - socket.on('equip',function(data){ - var player=players[socket.id]; if(!player) return; - player.c={}; - data.num=to_number(data.num); - if(data.num>=player.items.length) return fail_response("invalid"); - var item=player.items[data.num]; - if(!item) return fail_response("no_item"); - if(item.b) return fail_response("item_blocked"); - if(item.name=="placeholder") return fail_response("item_placeholder"); - var def=G.items[item.name],to_update="reopen+u+cid",resolve={num:data.num},resolve_type="data"; - def.iname=item.name; //just for orb name checks [08/10/16] - if(!def) return fail_response("no_item"); - // if(is_sdk) server_log("Trying to equip "+JSON.stringify(data)); - - if(data.slot && get_trade_slots(player).includes(data.slot) && !data.consume) - { - if(item.acl || item.v) return fail_response("item_locked"); - var slot=data.slot,price=round(min(99999999999,max(parseInt(data.price)||1,1))),minutes=1; - data.q=max(1,parseInt(data.q)||1); - if((item.q||1)0) disappearing_text(socket,player,"+"+amount,{color:"hp",xy:1,s:"hp"}); - else disappearing_text(socket,player,amount,{color:"hp",xy:1,s:"hp"}); - } - if(p[0]=="mp") disappearing_text(socket,player,"+"+amount,{color:"mp",xy:1,s:"mp"}); - if(p[0]=="xp") disappearing_text(socket,player,"+1M",{color:"1mxp",xy:1,s:"xp",size:"large"}),xp=true,timeout_ui=120,timeout=0.1; - }); - if(def.debuff) - { - for(var c in player.s) - { - if(G.conditions[c] && G.conditions[c].debuff && (!G.conditions[c].persistent || c=="hopsickness")) - delete player.s[c]; - } - } - player.hp=min(player.hp,player.max_hp); player.mp=min(player.mp,player.max_mp); - if(def.cooldown) timeout=def.cooldown; - player.last.potion=future_ms(timeout); - if(!xp) to_update="u+cid+reopen+nc"; - socket.emit("eval",{code:"pot_timeout("+(timeout_ui||timeout)+")"}); - } - else if(!data.consume) - { - // console.log(data); - var slot=data.slot||def.type,existing; - var comp=can_equip_item(player,def,slot); - if(comp=="no") - { - resend(player,"u+cid+reopen"); // if you drop something on an invalid slot, it stays there for a bit otherwise [29/08/22] - return fail_response("cant_equip"); - } - slot=comp; - existing=player.slots[slot]; - player.slots[slot]=player.items[data.num]; player.cslots[slot]=cache_item(player.slots[slot]); - if(existing && existing.b) existing=null; - player.items[data.num]=existing,player.citems[data.num]=cache_item(existing); - player.s.penalty_cd={ms:min(((player.s.penalty_cd&&player.s.penalty_cd.ms)||0)+120,120000)}; - resolve.slot=slot; - } - else - { - return fail_response("cant_consume"); - } - - if(to_update) resend(player,to_update); - success_response(resolve_type,resolve); - }); - socket.on('misc_npc',function(data){ - var player=players[socket.id]; if(!player) return; - var npc=players[data.npc]; if(!npc || !npc.npc) return; - if(simple_distance(player,npc)>1000) return socket.emit("game_response","distance"); - if(data.npc==NPC_prefix+"Marven" && npc.misc==true) - { - server_log("Here!"); - } - }); - socket.on('unequip',function(data){ - var player=players[socket.id]; - if(!player || !data.slot || !player.slots[data.slot]) return fail_response("invalid"); - if(data.slot=="elixir") return fail_response("cant"); - var item=player.slots[data.slot],done=false; - if(in_arr(data.slot,trade_slots) && item.giveaway!==undefined) return fail_response("giveaway"); - // an oversight allowed .giveaway items to be equipped, so now they can be unequipped from regular slots [04/11/21] - if(player.esize<=0 && !item.b) return fail_response("no_space"); - player.slots[data.slot]=null; player.cslots[data.slot]=null; - player.c={}; - if(!item.b) add_item(player,item,{announce:false}); - resend(player,"reopen+u+cid"); - success_response("data"); - }); - socket.on('secondhands',function(data){ - var player=players[socket.id]; if(!player) return; - if(simple_distance(G.maps.main.ref.secondhands,player)>500) return socket.emit("game_response","distance"); - socket.emit('secondhands',csold); - }); - socket.on('lostandfound',function(data){ - var player=players[socket.id]; if(!player) return; - if(data=="info") return socket.emit("game_response",{response:"lostandfound_info",gold:S.gold}); - if(!player.donation) return socket.emit("game_response","lostandfound_donate"); - if(simple_distance(G.maps.woffice.ref.lostandfound,player)>500) return socket.emit("game_response","distance"); - socket.emit('lostandfound',cfound); - }) - socket.on('split',function(data){ - var player=players[socket.id],num=data.num,item=player.items[data.num],quantity=min(max(parseInt(data.quantity)||0,1),item&&item.q||1); - if(!item) return fail_response("no_item"); - if(!G.items[item.name].s) return fail_response("invalid"); - quantity=min(quantity,G.items[item.name].s||1); - if(!player.esize) return fail_response("cant_space"); - if(item.q==quantity) return success_response(); - if(item.name=="placeholder") return fail_response("item_placeholder"); - if(item.l) return fail_response("item_locked"); - if(item.b) return fail_response("item_blocked"); - - consume(player,data.num,quantity); - - var new_item=cache_item(item); - if(item.q) new_item.q=quantity; - - for(var i=0;iplayer.gold) return socket.emit("game_response","not_enough_gold"); - var shells=Math.floor(gold/G.multipliers.shells_to_gold); - player.gold-=gold; - socket.emit("game_log","Gave "+to_pretty_num(gold)+" gold"); - appengine_call("bill_user",{auth:player.auth,amount:-shells,reason:"buy_shells",name:player.name},function(result){ - server_log("buy_with_cash: "+JSON.stringify(result)); - if(result.failed || !result.done) { socket.emit("game_log","Purchase failed"); return; } - player.cash=result.cash; - socket.emit("game_log","Received "+to_pretty_num(shells)+" shells"); + socket.on("upgrade", function (data) { + try { + var player = players[socket.id]; + var item = player.items[data.item_num]; + var scroll = player.items[data.scroll_num]; + var offering = player.items[data.offering_num]; + var result; + var ex = ""; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (player.q.upgrade) { + return socket.emit("game_response", "upgrade_in_progress"); + } + G.maps.main.upgrade.name = player.name; + if (!player.computer && simple_distance(G.maps.main.upgrade, player) > B.sell_dist) { + return socket.emit("game_response", { response: "distance", place: "upgrade", failed: true }); + } + if (!item) { + return socket.emit("game_response", "upgrade_no_item"); + } + if ( + offering && + !( + (G.items[offering.name] && G.items[offering.name].type == "offering") || + (G.items[offering.name] && G.items[offering.name].offering !== undefined && !(item.level > 0)) + ) + ) { + return socket.emit("game_response", "upgrade_invalid_offering"); + } + if (!scroll && !offering) { + return socket.emit("game_response", "upgrade_no_scroll"); + } + if ((item.level || 0) != data.clevel) { + return socket.emit("game_response", "upgrade_mismatch"); + } + var item_def = G.items[item.name]; + var scroll_def = scroll && G.items[scroll.name]; + var offering_def = offering && G.items[offering.name]; + var grade = calculate_item_grade(item_def, item); + if (!item_def.upgrade) { + return socket.emit("game_response", "upgrade_cant"); + } + if ( + scroll && + (!in_arr(scroll_def.type, ["uscroll", "pscroll"]) || + (scroll_def.type == "uscroll" && !item_def.upgrade) || + (scroll_def.type == "pscroll" && !item_def.stat) || + grade > scroll_def.grade) + ) { + if (grade == 4 && scroll_def.type == "uscroll") { + return socket.emit("game_response", { + response: "max_level", + level: item.level, + place: "upgrade", + failed: true, + }); + } + return socket.emit("game_response", "upgrade_incompatible_scroll"); + } - resend(player,"reopen+nc"); + var new_level = (item.level || 0) + 1; + var probability = 1; + var oprobability = 1; + var grace = 0; + var high = false; + var ograde = calculate_item_grade(item_def, { name: item.name, level: 0 }); + var tmult = 1; + if (ograde == 1) { + tmult = 1.5; + } else if (ograde == 2) { + tmult = 2; + } + + delete player.p.u_item; + delete player.p.u_type; + delete player.p.u_itemx; + delete player.p.u_roll; + delete player.p.u_fail; + delete player.p.u_level; + + player.p.u_level = item.level || 0; + + if (!scroll) { + if (G.items[offering.name] && G.items[offering.name].offering !== undefined) { + var chance = 0.16; + var ms = 2000; + if ( + G.items[offering.name].offering > ograde || + (G.items[offering.name].offering == 2 && calculate_item_value(item) <= 20000000) + ) { + chance = 0.32; + } + chance *= [2.8, 1.6, 1][ograde]; + if (G.items[offering.name].offering < ograde) { + return socket.emit("game_response", "upgrade_invalid_offering"); + } + if (data.calculate) { + return success_response("upgrade_chance", { + calculate: true, + chance: chance, + offering: offering.name, + item: cache_item(item), + grace: item.grace || 0, + }); + } + var result = Math.random(); + + consume_one(player, data.offering_num); + player.p.u_type = "normal"; + player.p.u_roll = result; + if (player.s.massproduction) { + ms /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + ms /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.upgrade = { ms: ms, len: ms, num: data.item_num, silent: true }; + player.items[data.item_num] = { + name: "placeholder", + p: { + chance: chance, + name: item.name, + level: item.level, + scroll: null, + offering: offering.name, + nums: [], + }, + }; + + if (result <= chance) { + item.p = "shiny"; + player.p.u_item = item; + } else { + player.p.u_item = item; + player.p.u_fail = true; + } + } else { + var ms = 1000; + if (data.calculate) { + return success_response("upgrade_chance", { + calculate: true, + chance: 1, + offering: offering.name, + item: cache_item(item), + grace: item.grace || 0, + }); + } + consume_one(player, data.offering_num); + item.grace = (item.grace || 0) + 0.5; + server_log("item.grace: " + item.grace); + player.p.u_type = "offering"; + player.p.u_item = item; + player.p.u_roll = 0.999999999999999; + if (player.s.massproduction) { + ms /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + ms /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; + player.items[data.item_num] = { + name: "placeholder", + p: { chance: 1, name: item.name, level: item.level, scroll: null, offering: offering.name, nums: [] }, + }; + } + } else if (scroll_def.type == "uscroll") { + if (item.l) { + return socket.emit("game_response", { response: "item_locked", place: "upgrade", failed: true }); + } + if (grade == 4) { + return socket.emit("game_response", { + response: "max_level", + level: item.level, + place: "upgrade", + failed: true, + }); + } + if (!data.calculate) { + consume_one(player, data.scroll_num); + } + oprobability = probability = D.upgrades[item_def.igrade][new_level]; + // grace=max(0,min(new_level+1, (item.grace||0) + min(3,player.p.ugrace[new_level]/3.0) +min(2,ugrace[new_level]/4.0) +item_def.igrace + player.p.ograce/2.0 )); - original [16/07/18] + grace = max( + 0, + min(new_level + 1, (item.grace || 0) + min(3, player.p.ugrace[new_level] / 4.5) + item_def.igrace) + + min(6, S.ugrace[new_level] / 3.0) + + player.p.ograce / 3.2, + ); + server_log( + "Grace num: " + + grace + + "\nItem: " + + (item.grace || 0) + + "\nPlayer: " + + min(3, player.p.ugrace[new_level] / 4.5) + + "\nDef: " + + item_def.igrace + + "\nOgrace:" + + player.p.ograce / 3.2 + + "\nS.ugrace: " + + min(6, S.ugrace[new_level] / 3.0), + ); + grace = (probability * grace) / new_level + grace / 1000.0; + server_log("Grace-prob: " + grace); + result = Math.random(); + server_log(result + " < " + probability); + + // if(!data.calculate && item.name=="throwingstars" && scroll_def.grade==2 && item.level==4) item.p="superfast"; + + if (scroll_def.grade > grade && new_level <= 10) { + probability = probability * 1.2 + 0.01; + high = true; + if (!data.calculate) { + item.grace = (item.grace || 0) + 0.4; + } + } + + if (offering) { + var increase = 0.4; + if (!data.calculate) { + consume_one(player, data.offering_num); + } + + if (offering_def.grade > grade + 1) { + probability = probability * 1.7 + grace * 4; + high = true; + increase = 3; + } else if (offering_def.grade > grade) { + probability = probability * 1.5 + grace * 1.2; + high = true; + increase = 1; + } else if (offering_def.grade == grade) { + probability = probability * 1.4 + grace; + } else if (offering_def.grade == grade - 1) { + probability = probability * 1.15 + grace / 3.2; + increase = 0.2; + } else { + probability = probability * 1.08 + grace / 4; + increase = 0.1; + } + + if (!data.calculate) { + item.grace = (item.grace || 0) + increase; + } // previously +1 [16/07/18] + } else { + grace = max(0, grace / 4.8 - 0.4 / ((new_level - 0.999) * (new_level - 0.999))); + probability += grace; // previously 12.0 // previously 9.0 [16/07/18] + } + + if (!data.calculate && Math.random() < 0.025) { + // Bonus grace + item.grace = (item.grace || 0) + 1; + } + + if (data.item_num == player.p.item_num && Math.random() < 0.6) { + // Added [29/10/17] + server_log("16 cheat"); + result = max(Math.random() / 10000.0, result * 0.975 - 0.012); + } + + if (high) { + probability = min(probability, min(oprobability + 0.36, oprobability * 3)); + } else { + probability = min(probability, min(oprobability + 0.24, oprobability * 2)); + } + server_log(result + " < " + probability + " grace: " + grace); + + if (data.calculate) { + return success_response("upgrade_chance", { + calculate: true, + chance: probability, + offering: (offering && offering.name) || undefined, + item: cache_item(item), + grace: item.grace || 0, + scroll: scroll.name, + }); + } + + if (gameplay == "test") { + result = 0; + } + // result=probability; + player.p.u_type = "normal"; + player.p.u_roll = result; + var ms = 500 * new_level * Math.sqrt(new_level) * tmult; + if (player.s.massproduction) { + ms /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + ms /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; + if (gameplay == "hardcore") { + player.q.upgrade.ms = player.q.upgrade.len = 500; + } + player.items[data.item_num] = { + name: "placeholder", + p: { + chance: probability, + name: item.name, + level: item.level, + scroll: scroll.name, + offering: offering && offering.name, + nums: [], + }, + }; + + //result=probability+EPS; + + if (result <= probability) { + // console.log("here"); + player.p.ugrace[new_level] = S.ugrace[new_level] = 0; + if (offering) { + player.p.ograce *= 0.25; + } else { + player.p.ograce *= 1 - new_level * 0.005; + } + item.level = new_level; + if (item.oo != player.name) { + item.o = player.name; + } + player.p.u_item = item; + if (parseInt(result * 10000) == parseInt(probability * 10000) && grade >= 1) { + add_item_property(item, "lucky"); + add_achievement(player, "lucky"); + } + } else { + player.p.ugrace[new_level - 1] += 1; + S.ugrace[new_level - 1] += 1; + player.p.ugrace[new_level] += 1; + S.ugrace[new_level] += 1; + if (new_level >= 8 && new_level <= 15) { + player.p.ugrace[new_level - 1] += 1; + S.ugrace[new_level - 1] += 1; + player.p.ugrace[new_level - 2] += 2 + ((offering && 1) || 0); + S.ugrace[new_level - 2] += 2; + player.p.ugrace[new_level - 3] += 2 + ((offering && 2) || 0); + S.ugrace[new_level - 3] += 3 + ((offering && 1) || 0); + } + if (offering) { + player.p.ograce += 0.6; + } // previously 1 [16/07/18] + if (scroll_def.grade != 3.6) { + player.p.u_itemx = item; + } else { + player.p.u_item = item; + player.p.u_fail = true; + } + if (parseInt(result * 10000) == parseInt(probability * 10000)) { + if (player.esize && grade >= 1) { + add_item(player, "essenceofgreed"); + } + add_achievement(player, "unlucky"); + } + } + } else if (scroll_def.type == "pscroll") { + var needed = [1, 10, 100, 1000, 9999, 9999, 9999]; + if (scroll.q < needed[grade]) { + return socket.emit("game_response", { response: "upgrade_scroll_q", q: needed[grade], h: scroll.q }); + } + if (!data.calculate) { + consume(player, data.scroll_num, needed[grade]); + } + + if (data.calculate) { + return success_response("upgrade_chance", { + calculate: true, + chance: 0.99999, + scroll: scroll.name, + item: cache_item(item), + grace: item.grace || 0, + }); + } + + if (offering) { + consume_one(player, data.offering_num); + item.grace = (item.grace || 0) + 1; + server_log("Graced up to " + item.grace); + } + + item.stat_type = scroll_def.stat; + player.p.u_roll = Math.random(); + + player.p.u_type = "stat"; + if (player.p.u_roll <= 0.99999 || offering) { + player.p.u_item = item; + } else { + player.p.u_itemx = item; + } + var ms = 2000 * tmult * tmult; + if (player.s.massproduction) { + ms /= 2; + delete player.s.massproduction; + ex = "+u+cid"; + } + if (player.s.massproductionpp) { + ms /= 10; + delete player.s.massproductionpp; + ex = "+u+cid"; + } + player.q.upgrade = { ms: ms, len: ms, num: data.item_num }; + player.items[data.item_num] = { + name: "placeholder", + p: { + chance: 0.99999, + name: item.name, + level: item.level, + scroll: scroll.name, + offering: offering && offering.name, + nums: [], + }, + }; + } + + player.citems[data.item_num] = cache_item(player.items[data.item_num]); + + resend(player, "reopen+nc+inv" + ex); + } catch (e) { + server_log("upgrade_e " + e); + return socket.emit("game_response", { response: "exception", place: "upgrade", failed: true }); + } }); - resend(player,"reopen+nc"); - }); - socket.on('buy_with_cash',function(data){ - var player=players[socket.id],name=data.name,def=G.items[data.name],quantity=min(max(parseInt(data.quantity)||0,1),def&&def.s||9999); - if(!player || player.user || gameplay=="hardcore" || gameplay=="test") return fail_response("cant_in_bank"); - var cost=def.cash*quantity; - if(!def.cash || def.ignore) return fail_response("invalid"); - if(def.p2w) return fail_response("invalid"); - if(!def.s) quantity=1; - if(!can_add_item(player,create_new_item(name,quantity))) return fail_response("no_space"); - appengine_call("bill_user",{auth:player.auth,amount:cost,reason:data.name,name:player.name},function(result){ - server_log("buy_with_cash: "+JSON.stringify(result)); - if(result.failed || !result.done) { socket.emit("game_log","Purchase failed"); return; } - player.cash=result.cash; - var new_item=create_new_item(name,quantity),done=false; - add_item(player,new_item,{announce:false}); - socket.emit("game_log","Spent "+to_pretty_num(cost)+" shells"); - - resend(player,"reopen+nc+inv"); - }); - success_response({success:false,in_progress:true}); - }); - socket.on('sbuy',function(data){ - var player=players[socket.id],done=false,npc=G.maps.main.ref.secondhands,c=S.sold,cc=csold,e="+$p",ev="secondhands",mult=2,src="scnd"; - if(data.f) npc=G.maps.woffice.ref.lostandfound,c=S.found,cc=cfound,e="+$f",ev="lostandfound",mult=4,src="lost"; - if(!player || player.user) return game_response("cant_in_bank"); - if(player.s.hopsickness && ev=="lostandfound") return fail_response("cant_when_sick",{goblin:true}); - if(simple_distance(npc,player)>500) return socket.emit("game_response","distance"); - for(var i=0;iplayer.gold) return socket.emit("game_response","buy_cost"); - player.gold-=gold; - item.src=src; - add_item(player,c[i],{announce:false}); - c.splice(i,1); - cc.splice(i,1); - socket.emit("game_log","Spent "+to_pretty_num(gold)+" gold"); - resend(player,"reopen+nc+inv"); - socket.emit(ev,cc); - done=true; - xy_emit(npc,"ui",{type:e,id:npc.id,name:player.name,item:cache_item(item,1)}); - break; + socket.on("equip", function (data) { + var player = players[socket.id]; + if (!player) { + return; } - } - if(!done) return socket.emit("game_log","Item gone"); - }); - socket.on('buy',function(data){ - var player=players[socket.id],can_reach=false; - if(!player || player.user) return fail_response("cant_in_bank"); - var name=data.name,quantity=min(max(parseInt(data.quantity)||0,1),G.items[name]&&G.items[name].s||9999),cost=0,added=false,done=false; - if(!can_buy[name] && gameplay!="test") return fail_response("buy_cant_npc"); - if(!can_add_item(player,create_new_item(name,quantity))) return fail_response("buy_cant_space"); - (G.maps[player.map].items[name]||[]).forEach(function(l){ - if(simple_distance(player,l)B.dist || receiver.map!=player.map)) return fail_response("distance"); - if(data.num!==undefined) - { - data.num=max(0,parseInt(data.num)||0); - var item=player.items[data.num]; - if(!item) return fail_response("send_no_item"); - if(item.name=="placeholder") return fail_response("item_placeholder"); - if(item.l || item.acl && receiver.owner!=player.owner) return fail_response("item_locked"); - if(item.b) return fail_response("item_blocked"); - data.q=min((item.q||1),max(1,parseInt(data.q||1)||1)); - if(!data.q) return fail_response("no_item"); - if(!can_add_item(receiver,create_new_item(item.name,data.q))) return fail_response("send_no_space"); - if((item.q||1)==data.q) player.items[data.num]=player.citems[data.num]=null,player.esize++; - else - { - player.items[data.num].q-=data.q; - player.citems[data.num]=cache_item(player.items[data.num]); + player.c = {}; + data.num = to_number(data.num); + if (data.num >= player.items.length) { + return fail_response("invalid"); } - - if(item.q) - { - s_item=create_new_item(item.name,data.q); - if(item.v) s_item.v=item.v; - if(item.data) s_item.data=item.data; - num=add_item(receiver,s_item,{announce:false}); + var item = player.items[data.num]; + if (!item) { + return fail_response("no_item"); } - else - { - s_item=item; - num=add_item(receiver,item,{announce:false}); + if (item.b) { + return fail_response("item_blocked"); } - - if(receiver.owner!=player.owner) item.src="snd"; - - - add_to_history(player,{"name":"item",to:receiver.name,item:item.name,q:data.q,level:item.level}); - add_to_history(receiver,{"name":"item",from:player.name,item:item.name,q:data.q,level:item.level}); - - xy_emit(player,"ui",{type:"item_sent",receiver:receiver.name,sender:player.name,item:cache_item(s_item,null,{q:data.q}),num:num,fnum:data.num,event:true}); - - resend(player,"reopen+nc+inv"); resend(receiver,"reopen+nc+inv"); - receiver.socket.emit("game_response",{response:"item_received",name:player.name,item:item.name,q:data.q,num:num,cevent:true}); - player.socket.emit("game_response",{response:"item_sent",name:receiver.name,item:item.name,q:data.q,num:data.num,cevent:true,place:"send"}); - } - else if(data.gold!==undefined) - { - data.gold=min(player.gold,max(1,parseInt(data.gold||1)||1))||player.gold; - if(!data.gold) return fail_response("invalid"); - player.gold-=data.gold; - if(!is_same(player,receiver) && data.gold!=1) data.gold=parseInt(data.gold*0.975); - receiver.gold+=data.gold; - - add_to_history(player,{"name":"gold",to:receiver.name,amount:data.gold}); - add_to_history(receiver,{"name":"gold",from:player.name,amount:data.gold}); - - xy_emit(player,"ui",{type:"gold_sent",receiver:receiver.name,sender:player.name,gold:data.gold,event:true}); - - resend(player,"reopen+nc+inv"); resend(receiver,"reopen+nc+inv"); - receiver.socket.emit("game_response",{response:"gold_received",name:player.name,gold:data.gold,cevent:true}); - player.socket.emit("game_response",{response:"gold_sent",name:receiver.name,gold:data.gold,cevent:true,place:"send"}); - } - else if(data.cx!==undefined) - { - var cxl=map_cx(player),count=all_cx(player,1); - if(!(cxl[data.cx] && player.p.acx[cxl[data.cx]] && count[data.cx]>0)) return fail_response("send_no_cx"); - // count[cxl[data.cx]] before [28/01/22] - if(receiver.owner!=player.owner) return fail_response("send_diff_owner"); - player.p.acx[cxl[data.cx]]-=1; - if(!player.p.acx[cxl[data.cx]]) delete player.p.acx[cxl[data.cx]]; - receiver.p.acx[cxl[data.cx]]=(receiver.p.acx[cxl[data.cx]]||0)+1; - - xy_emit(player,"ui",{type:"cx_sent",receiver:receiver.name,sender:player.name,cx:data.cx,event:true}); - - receiver.socket.emit("game_response",{response:"cx_received",name:player.name,cx:data.cx,acx:receiver.p.acx,cevent:true}); - player.socket.emit("game_response",{response:"cx_sent",name:receiver.name,cx:data.cx,acx:player.p.acx,cevent:true,place:"send"}); - } - }); - socket.on('donate',function(data){ - var player=players[socket.id],XPX=3.2; - if(!player || player.user) return game_response("cant_in_bank"); - var gold=max(1,min(parseInt(data.gold)||0,1000000000)); - if(gold>player.gold) return socket.emit("game_response","gold_not_enough"); - if(gold>=1000000) response="thx",player.donation=true; - else if(gold<100000) response="low"; - else add_item(player,"gum"),response="gum"; - if(S.gold<500000000) XPX=4.8; - else if(S.gold<=1000000000) XPX=4; - player.gold-=gold; - S.gold+=gold; - if(gold>=5000000) lstack(S.logs.donate,{name:player.name,gold:gold,xp:XPX}); - if(player.type=="merchant") - player.xp+=parseInt(gold*XPX); - resend(player,"reopen"); - socket.emit("game_response",{response:"donate_"+response,gold:gold,xprate:XPX}); - }); - socket.on('destroy',function(data){ - var player=players[socket.id],add="+nc+inv"; - data.num=max(0,parseInt(data.num)||0); - if(!player.items[data.num]) return fail_response("no_item"); - var item=player.items[data.num],name=player.items[data.num].name; - data.q=min(max(parseInt(data.q)||0,1),item&&item.q||1); - if(item.name=="placeholder") return fail_response("item_placeholder"); - if(item.l) return fail_response("item_locked"); - if(item.b) return fail_response("item_blocked"); - if(item.level!=13) - { - consume(player,data.num,data.q); - //player.items[data.num]=player.citems[data.num]=null; - } - if(data.statue) - { - if(item.name=="shadowstone") - { - add="+u+cid"; - player.s.invis={ms:99999}; + if (item.name == "placeholder") { + return fail_response("item_placeholder"); } - if(G.items[item.name].upgrade && Math.random()<1.0/(gameplay=="hardcore"&&10000||1000000)) - { - add="+u+cid"; - item.level=13; - player.items[data.num]=item; - player.citems[data.num]=cache_item(player.items[data.num]); + var def = G.items[item.name]; + var to_update = "reopen+u+cid"; + var resolve = { num: data.num }; + var resolve_type = "data"; + def.iname = item.name; //just for orb name checks [08/10/16] + if (!def) { + return fail_response("no_item"); } - xy_emit(G.maps.spookytown.ref.poof,"upgrade",{type:"poof",success:1}); - } - resend(player,"reopen"+add); - success_response("destroyed",{name:name,place:"destroy",cevent:"destroy"}); - }); - socket.on('join_giveaway',function(data){ - var player=players[socket.id],seller=players[id_to_id[data.id]],num; - if(!player || player.user) return fail_response("cant_in_bank"); - if(!in_arr(data.slot,trade_slots)) return fail_response("invalid"); - if(!seller || seller.npc || is_invis(seller)) return fail_response("seller_gone"); - if(seller.user) return fail_response("cant_in_bank") - if(distance(seller,player,true)>B.dist || seller.map!=player.map) return fail_response("distance"); - if(seller.id==player.id) return fail_response("hmm"); - var item=seller.slots[data.slot]; - if(!item || (data.rid && item.rid!=data.rid)) return fail_response("item_gone"); - if(!item.giveaway) return fail_response("sneaky"); - if(!player.auth_id) return fail_response("need_auth"); - //if(item.list.includes(player.name)) return socket.emit("game_log","Already joined!"); - //if(player.type!="merchant") return socket.emit("game_log","Only merchants can join giveaways!"); + // if(is_sdk) server_log("Trying to equip "+JSON.stringify(data)); - //item.list.push(player.name); - - item.registry=item.registry||{}; - item.registry[player.auth_id]=player.name; - item.list=Object.values(item.registry); - - socket.emit("game_log","Joined the giveaway!"); - seller.socket.emit("game_response",{response:"giveaway_join",name:player.name,slot:data.slot,cevent:true}); - - resend(player,"reopen"); resend(seller,"reopen+u+cid"); - success_response({}); - }); - socket.on('trade_wishlist',function(data){ - var player=players[socket.id]; if(!player) return; - data.q=min(9999,max(1,parseInt(data.q||1)||1)); - if(!in_arr(data.slot,trade_slots) || !G.items[data.name] || data.name=="placeholder") return fail_response("invalid"); - if(player.slots[data.slot] && !player.slots[data.slot].b) return fail_response("slot_occuppied"); - if(!get_trade_slots(player).includes(data.slot)) return fail_response("invalid"); - var item={name:data.name,rid:randomStr(4),price:round(min(99999999999,max(parseInt(data.price)||1,1))),b:true}; - if(G.items[data.name].upgrade || G.items[data.name].compound) item.q=min(99,data.q),item.level=round(min(12,max(parseInt(data.level)||0,0))); - else item.q=data.q; - player.slots[data.slot]=item; player.cslots[data.slot]=cache_item(player.slots[data.slot],true); - resend(player,"reopen+u+cid+nc"); - success_response({}) - }); - socket.on('trade_sell',function(data){ - // if(!is_sdk) return; - var player=players[socket.id],buyer=players[id_to_id[data.id]],num,actual=null,num=null; - data.q=max(1,parseInt(data.q||1)||1); - if(!player || player.user) return fail_response("cant_in_bank"); - if(!in_arr(data.slot,trade_slots)) return fail_response("invalid"); - if(!buyer || buyer.npc || is_invis(buyer)) return fail_response("buyer_gone"); - if(player.user) return fail_response("cant_in_bank"); - if(distance(buyer,player,true)>B.dist) return fail_response("distance"); - if(buyer.id==player.id) return fail_response("hmm"); - var item=buyer.slots[data.slot]; - if(!item || (data.rid && item.rid!=data.rid)) return fail_response("item_gone"); - if(item.name=="placeholder") return fail_response("item_placeholder"); - if(!item.b && !B.rbugs) return fail_response("sneaky"); - if((item.q||1)buyer.gold) return fail_response("buyer_gold"); - for(var i=0;i=data.q && player.items[i].level==item.level && !player.items[i].l) - { - actual=player.items[i]; num=i; break; - } - } - if(!actual) return fail_response("no_item"); - if(actual.b) return fail_response("item_blocked"); - if(!can_add_item(buyer,create_new_item(item.name,data.q))) return fail_response("trade_bspace"); - var price=item.price*data.q; - player.gold+=round(price*(1-player.tax)); add_to_trade_history(player,"sell",buyer.name,cache_item(actual,true,{q:data.q}),price); - buyer.gold-=price; add_to_trade_history(buyer,"buy",player.name,cache_item(actual,true,{q:data.q}),price); - S.gold+=price-round(price*(1-player.tax)); - - if((item.q||1)==data.q) buyer.slots[data.slot]=buyer.cslots[data.slot]=null; - else - { - buyer.slots[data.slot].q-=data.q; - buyer.cslots[data.slot]=cache_item(buyer.slots[data.slot],true); - } - - // up to here - - if((player.items[i].q||1)==data.q) player.items[num]=player.citems[num]=null; - else player.items[num].q-=data.q,player.citems[num]=cache_item(player.items[num]); - - if(G.items[item.name].s) bnum=add_item(buyer,create_new_item(item.name,data.q),{announce:false}); - else bnum=add_item(buyer,actual,{announce:false}); - - if(player.type=="merchant") merchant_xp_logic(player,buyer,price,price-round(price*(1-player.tax))); - if(buyer.type=="merchant") merchant_xp_logic(buyer,player,price,price-round(price*(1-buyer.tax))); - - socket.emit("game_log","Sales tax "+to_pretty_num(price-round(price*(1-player.tax)))+" gold ["+(player.tax*100)+"%]"); - socket.emit("game_log","Received "+to_pretty_num(round(price*(1-player.tax)))+" gold"); - buyer.socket.emit("game_log","Spent "+to_pretty_num(price)+" gold"); - - xy_emit(buyer,"ui",{type:"+$$",seller:player.name,buyer:buyer.name,item:cache_item(actual,true,{q:data.q,price:item.price}),slot:data.slot,num:bnum,snum:num}); - - resend(player,"reopen"); resend(buyer,"reopen+u+cid"); - success_response({}); - }); - socket.on('trade_buy',function(data){ - var player=players[socket.id],seller=players[id_to_id[data.id]],num; - data.q=max(1,parseInt(data.q||1)||1); - if(!player || player.user) return fail_response("cant_in_bank"); - if(!in_arr(data.slot,trade_slots)) return fail_response("invalid"); - if(!seller || seller.npc || is_invis(seller)) return fail_response("seller_gone"); - if(seller.user) return fail_response("cant_in_bank"); - if(distance(seller,player,true)>B.dist || seller.map!=player.map) return fail_response("distance"); - if(seller.id==player.id) return fail_response("hmm"); - var item=seller.slots[data.slot]; - if(!item || (data.rid && item.rid!=data.rid)) return fail_response("item_gone"); - if(item.name=="placeholder") return fail_response("item_placeholder"); - if(item.b || item.giveaway) return fail_response("sneaky"); - if(item.price*data.q>player.gold) return fail_response("gold_not_enough"); - if((item.q||1)=player.items.length) player.items.push(null); [22/11/16] - if(player.items[a] && player.items[a].name=="placeholder") return fail_response("item_placeholder"); - if(player.items[b] && player.items[b].name=="placeholder") return fail_response("item_placeholder"); - if(can_stack(player.items[a],player.items[b])) - { - player.items[data.a].q=(player.items[a].q||1)+(player.items[b].q||1); - player.items[data.b]=null; - player.esize++; - } - else - { - a_item=player.items[a]; - player.items[a]=player.items[b]; - player.items[b]=a_item; - } - player.citems[data.a]=cache_item(player.items[data.a]); player.citems[data.b]=cache_item(player.items[data.b]); - resend(player,"reopen+nc+inv"); - return success_response("data"); - }); - socket.on('bank',function(data){ - var player=players[socket.id]; if(!player) return; - if(!player.user || player.mounting || player.unmounting) return fail_response("bank_unavailable"); - var success={}; - if(data.operation=="withdraw") - { - var amount=max(0,min(parseInt(data.amount)||0,player.user.gold)); - player.user.gold-=amount; - player.gold+=amount; - success={response:"bank_withdraw",gold:amount,cevent:true}; - } - if(data.operation=="deposit") - { - var amount=max(0,min(parseInt(data.amount)||0,player.gold)); - player.user.gold+=amount; - player.gold-=amount; - success={response:"bank_store",gold:amount,cevent:true}; - } - if(data.operation=="unlock") - { - if(!bank_packs[data.pack]) return fail_response("invalid"); - var gold=bank_packs[data.pack][1],shells=bank_packs[data.pack][2]; - if(!gold) return fail_response("gold_not_enough"); - if(player.user[data.pack]) return fail_response("invalid"); - if(data.gold) - { - if(player.goldplayer.str*3) fail_response("too_far"); - consume_one(player,data.num); - if(item.name=="confetti") - { - player.thrilling=future_s(20); - xy_emit({map:player.map,in:player.in,x:x,y:y},"eval","confetti_shower({in:'"+player.in+"',map:'"+player.map+"',real_x:"+x+",real_y:"+y+"})"); - } - if(item.name=="firecrackers") - { - player.thrilling=future_s(200); - xy_emit({map:player.map,in:player.in,x:x,y:y},"eval","firecrackers({in:'"+player.in+"',map:'"+player.map+"',real_x:"+x+",real_y:"+y+"})"); - for(var id in instances[player.in].monsters) - { - var monster=instances[player.in].monsters[id]; - if(monster.target && distance({map:player.map,in:player.in,x:x,y:y},monster)<64) stop_pursuit(monster,{force:true,cause:"firecrackers"}); + if (item.l) { + return fail_response("item_locked"); } - } - if(item.name=="whiteegg") - { - xy_emit({map:player.map,in:player.in,x:x,y:y},"eval","egg_splash("+x+","+y+")"); - for(var id in instances[player.in].monsters) - { - var monster=instances[player.in].monsters[id]; - if(!monster.target && distance({map:player.map,in:player.in,x:x,y:y},monster)<32) - target_player(monster,player); - } - } - if(item.name=="smoke") - { - xy_emit({map:player.map,in:player.in,x:x,y:y},"eval","assassin_smoke("+x+","+y+");"); - } - resend(player,"reopen+nc+inv"); - success_response({}); - } - else fail_response("no_item"); - }); - socket.on('poke',function(data){ - var player=players[socket.id],level=1; - if(!player || !player.slots.gloves || player.slots.gloves.name!="poker") return; - if(player.pokes>=50) return socket.emit("game_log","You are out of pokes!"); - player.pokes=(player.pokes||0)+1; - if(player.slots.gloves.level>=10) level=4; - else if(player.slots.gloves.level==9) level=3; - else if(player.slots.gloves.level==8) level=2; - xy_emit(player,"poke",{name:data.name,level:level,who:player.name}); - }); - socket.on('merge',function(data){ - var player=players[socket.id]; if(!player) return; - var container=player.slots[data.container]; - var pet=player.slots[data.pet]; - if(!container || container.type!="container" || !pet || pet.type!="container") return socket.emit("game_response","merge_mismatch"); - if(G.items[container.name].grade=8 && (player.type=="mage" || player.type=="priest")) player.tskin="snow_angel"; - else { return socket.emit("game_response","nothing"); } - } - else if(item.name=="tristone") - { - var on=true; - if(player.tskin || player.tactivations==100) player.tskin="",on=false; - else if(item.level<=1 && deduct_gender(player)=="female") player.tskin=random_one(["tf_green","tf_pink"]); - else if(item.level==2 && deduct_gender(player)=="female") player.tskin=random_one(["tf_blue","tf_purple"]); - else if(deduct_gender(player)=="female") player.tskin="tf_orange"; - else if(item.level<=1) player.tskin=random_one(["tm_gray","tm_brown","tm_white"]); - else if(item.level==2) player.tskin=random_one(["tm_green","tm_yellow","tm_purple"]); - else player.tskin=random_one(["tm_blue","tm_red"]); - player.tactivations=(player.tactivations||0)+1; - } - else if(item.name=="darktristone") - { - var on=true; - if(player.tskin || player.tactivations==100) player.tskin="",on=false; - else if(item.level==4 && deduct_gender(player)=="female") player.tskin="mf_blue"; - else if(deduct_gender(player)=="female") player.tskin="mf_yellow"; - else if(item.level==4) player.tskin="mm_blue"; - else player.tskin="mm_yellow"; - player.tactivations=(player.tactivations||0)+1; - } - else return; - resend(player,"reopen+nc+inv+u+cid"); - } - else - { - var item=player.items[data.num]; - if(item) - { - if(item.name=="frozenstone") - { - consume_one(player,data.num); - } - if(item.name=="bkey") - { - if(!player.user) return socket.emit("game_response","only_in_bank"); - if(player.user.unlocked && player.user.unlocked.bank_b) return socket.emit("game_response","already_unlocked"); - consume_one(player,data.num); - player.user.unlocked=player.user.unlocked||{}; - player.user.unlocked.bank_b=new Date(); - player.user.items8=player.user.items8||[]; - socket.emit("game_response","door_unlocked"); - } - if(item.name=="ukey") - { - if(!player.user) return socket.emit("game_response","only_in_bank"); - if(player.user.unlocked && player.user.unlocked.bank_u) return socket.emit("game_response","already_unlocked"); - consume_one(player,data.num); - player.user.unlocked=player.user.unlocked||{}; - player.user.unlocked.bank_u=new Date(); - player.user.items24=player.user.items24||[]; - socket.emit("game_response","door_unlocked"); - } - if(item.name=="dkey") - { - if(!player.user) return socket.emit("game_response","only_in_bank"); - var found=false; - for(var i=0;i<48;i++) - { - var pack="items"+i; - if(!bank_packs[pack] || player.user[pack]) continue; - found=true; - player.user[pack]=[]; player.cuser[pack]=[]; - break; + var timeout = 2000; + var timeout_ui = null; + var xp = false; + consume_one(player, data.num); + (def.gives || []).forEach(function (p) { + var amount = p[1]; + if (player.s.poisoned) { + amount = round(amount / 2); } - if(!found) return; - consume_one(player,data.num); - socket.emit("game_response","bank_pack_unlocked"); - } - } - resend(player,"reopen+nc+inv"); - } - }); - socket.on('booster',function(data){ - var player=players[socket.id],item=player.items[data.num]; if(!player) return; - server_log("booster "+data.num+" "+data.action); - if(!item || !in_arr(item.name,booster_items)) return fail_response("invalid"); - if(data.action=="activate" && !item.expires) - { - item.expires=new Date(); - item.expires.setDate(item.expires.getDate()+30+(item.level||0)*2); - //item.expires.setMinutes(item.expires.getMinutes()+2); - } - else if(data.action=="shift") - { - if(!in_arr(data.to,booster_items)) return fail_response("invalid"); - player.xpm=player.goldm=player.luckm=1; - item.name=data.to; - player.s.penalty_cd={ms:min(((player.s.penalty_cd&&player.s.penalty_cd.ms)||0)+240,120000)}; - } - // if(item.expires) item.p="legacy"; - player.citems[data.num]=cache_item(player.items[data.num]); - resend(player,"reopen"); - success_response({name:player.items[data.num].name}); - }); - socket.on('convert',function(data){ - // There are a lot of routines that could give birth to an endgame bug - // This routine ended up being the one - // "stone" boosters were originally 1200 shells - // when they were discontinued, I let players exchange them for 3600 shells instead - // turns out 'buy_with_cash' didn't check whether an item has "ignore" or not - // so one could buy the stones for 1200 shells and sell them for 3600 - // unlocking infinite gold and shells [17/01/18] - var player=players[socket.id],item=player.items[data.num],amount=3600; - server_log("stone "+data.num+" "+data.action); - if(!item || !in_arr(item.name,["stoneofxp","stoneofgold","stoneofluck"])) return; - if(item.expires) - { - amount=round(600-hsince(item.expires)*100/24.0); - } - add_shells(player,amount,"convert"); - player.items[data.num]=null; - player.citems[data.num]=cache_item(player.items[data.num]); - resend(player,"reopen+cid"); - }); - socket.on('emotion',function(data){ - var player=players[socket.id]; if(!player) return; - if(!data.name) data.name=random_one(Object.keys(player.p.emx)); - if(player.last.emotion && mssince(player.last.emotion)<2000) - return socket.emit("game_response","emotion_cooldown"); - player.last.emotion=new Date(); - if(!G.emotions[data.name] || !player.p.emx[data.name]) - return socket.emit("game_response","emotion_cant"); - xy_emit(player,"emotion",{name:data.name,player:player.name}); - }); - socket.on('skill',function(data){ - var player=players[socket.id]; if(!player) return; - var target=null,cool=true,resolve={response:"data",place:data.name,success:true},reject=null; - player.first=true; G.skills.attack.cooldown=player.attack_ms; - server_log("skill "+JSON.stringify(data)); - if(is_disabled(player)) - return fail_response("disabled",data.name); - if((player.tskin=="konami" || is_silenced(player)) && data.name!="attack") - return socket.emit("game_response",{response:"skill_cant_incapacitated",place:data.name,failed:true}); - if(!G.skills[data.name]) return socket.emit("game_response",{response:"no_skill",place:data.name,failed:true}); - if(G.skills[data.name].mp && player.mpB.max_vision)) - return socket.emit("disappear",{id:data.id,place:data.name,reason:"not_there"}); - } - if(G.skills[data.name].requirements) - { - for(var requirement in G.skills[data.name].requirements) - { - if(!player[requirement] || player[requirement] 0) { + disappearing_text(socket, player, "+" + amount, { color: "hp", xy: 1, s: "hp" }); + } else { + disappearing_text(socket, player, amount, { color: "hp", xy: 1, s: "hp" }); + } } - found=true; - } - }); - if(charges) return socket.emit("game_response",{response:"skill_cant_charges",place:data.name,failed:true}); - if(!found) return socket.emit("game_response",{response:"skill_cant_slot",place:data.name,failed:true}); - } - if(player.s.invincible) {delete player.s.invincible; player.to_resend="u+cid"}; - if(data.name=="attack" || data.name=="heal") - { - var attack=commence_attack(player,target,data.name); - if(!attack.failed) resolve=attack; - else reject=attack,cool=false; - } - if(data.name=="invis" && !player.s.invis) - { - if(player.s.marked) return socket.emit("game_response",{response:"skill_cant_use",place:data.name,failed:true}); - player.s.invis={ms:999999999999999}; - xy_emit(player,"disappear",{id:player.id,invis:true,reason:"invis"}); - player.to_resend=" "; - } - if(data.name=="pickpocket") - { - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - - consume_mp(player,G.skills[data.name].mp); - player.c[data.name]={ms:G.skills[data.name].duration_min+Math.random()*(G.skills[data.name].duration_max-G.skills[data.name].duration_min),target:target.name}; - player.to_resend="u+cid"; - resolve={response:"data",place:data.name,success:false,in_progress:true} - } - if(data.name=="fishing" || data.name=="mining") - { - var direction=0,the_zone=null; - consume_mp(player,G.skills[data.name].mp); - (G.maps[player.map].zones||[]).forEach(function(zone){ - if(zone.type!=data.name || the_zone) return; - [[0,-24,3],[-24,0,1],[24,0,2],[0,24,0]].forEach(function(m){ - if(is_point_inside([player.x+m[0],player.y+m[1]],zone.polygon)) - { - the_zone=zone; - direction=m[2]; + if (p[0] == "mp") { + disappearing_text(socket, player, "+" + amount, { color: "mp", xy: 1, s: "mp" }); + } + if (p[0] == "xp") { + disappearing_text(socket, player, "+1M", { color: "1mxp", xy: 1, s: "xp", size: "large" }); + xp = true; + timeout_ui = 120; + timeout = 0.1; } }); - }); - if(!the_zone || player.moving) - { - xy_emit(player,"ui",{type:data.name+"_fail",name:player.name}); - reject={response:"data",place:data.name}; - } - else - { - xy_emit(player,"ui",{type:data.name+"_start",name:player.name,direction:direction}); - player.c[data.name]={ms:G.skills[data.name].duration_min+Math.random()*(G.skills[data.name].duration_max-G.skills[data.name].duration_min),drop:the_zone.drop}; - } - resolve={response:"data",place:data.name,success:false,in_progress:true} - player.to_resend="u+cid"; - } - if(data.name=="light") - { - consume_mp(player,G.skills[data.name].mp); - xy_emit(player,"light",{name:player.name}); - player.to_resend="u+cid"; - } - if(data.name=="charge") - { - player.s.charging={ms:G.skills.charge.duration}; - player.to_resend="u+cid"; - } - if(data.name=="dash") - { - //console.log(data); - consume_mp(player,G.skills[data.name].mp); - var x=parseFloat(data.x)||0,y=parseFloat(data.y)||0,point=true; - if(point_distance(player.x,player.y,x,y)>50) - { - point=false; - player.socket.emit("game_response","dash_failed"); - reject={response:"data",place:data.name}; - } - if(point) - { - var spot=safe_xy_nearby(player.map,x,y); - if(!spot) spot=safe_xy_nearby(player.map,player.x+(x-player.x)*0.8,player.y+(y-player.y)*0.8); - if(!spot) spot=safe_xy_nearby(player.map,player.x+(x-player.x)*0.6,player.y+(y-player.y)*0.6); - if(!spot || point_distance(player.x,player.y,spot.x,spot.y)<10) - { - point=false; - player.socket.emit("game_response","dash_failed"); - reject={response:"data",place:data.name}; + if (def.debuff) { + for (var c in player.s) { + if (G.conditions[c] && G.conditions[c].debuff && (!G.conditions[c].persistent || c == "hopsickness")) { + delete player.s[c]; + } + } } - if(point) - { - //console.log([player.x,player.y,spot.x,spot.y]); - player.s.dash={ms:1000}; player.speed=500; - player.going_x=spot.x; - player.going_y=spot.y; - start_moving_element(player); - player.to_resend="u+cid"; - player.socket.emit("eval",{code:"ui_move("+spot.x+","+spot.y+")"}); + player.hp = min(player.hp, player.max_hp); + player.mp = min(player.mp, player.max_mp); + if (def.cooldown) { + timeout = def.cooldown; } + player.last.potion = future_ms(timeout); + if (!xp) { + to_update = "u+cid+reopen+nc"; + } + socket.emit("eval", { code: "pot_timeout(" + (timeout_ui || timeout) + ")" }); + } else if (!data.consume) { + // console.log(data); + var slot = data.slot || def.type; + var existing; + var comp = can_equip_item(player, def, slot); + if (comp == "no") { + resend(player, "u+cid+reopen"); // if you drop something on an invalid slot, it stays there for a bit otherwise [29/08/22] + return fail_response("cant_equip"); + } + slot = comp; + existing = player.slots[slot]; + player.slots[slot] = player.items[data.num]; + player.cslots[slot] = cache_item(player.slots[slot]); + if (existing && existing.b) { + existing = null; + } + player.items[data.num] = existing; + player.citems[data.num] = cache_item(existing); + player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 120, 120000) }; + resolve.slot = slot; + } else { + return fail_response("cant_consume"); } - } - if(data.name=="hardshell" || data.name=="power" || data.name=="xpower") - { - consume_mp(player,G.skills[data.name].mp); - player.s[G.skills[data.name].condition]={ms:G.skills[data.name].duration||G.conditions[G.skills[data.name].condition].duration}; - player.to_resend="u+cid"; - } - if(data.name=="mshield") - { - consume_mp(player,G.skills[data.name].mp); - if(player.s[G.skills[data.name].condition]) delete player.s[G.skills[data.name].condition]; - else player.s[G.skills[data.name].condition]={ms:999999999}; - player.to_resend="u+cid"; - } - if(data.name=="mcourage" || data.name=="mfrenzy" || data.name=="massproduction" || data.name=="massproductionpp") - { - consume_mp(player,G.skills[data.name].mp); - player.s[G.skills[data.name].condition]={ms:G.conditions[G.skills[data.name].condition].duration}; - xy_emit(player,"ui",{type:data.name,name:player.name}); - player.to_resend="u+cid"; - } - if(data.name=="throw") - { - if(distance(player,target,true)>G.skills[data.name].range+player.level) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - if(target.s.invincible || target.immune) - return socket.emit("game_response",{response:"target_invincible",place:data.name,failed:true}); - if(!player.items[data.num]) - return socket.emit("game_response",{response:"skill_no_item",place:data.name,failed:true}); - var item=player.items[data.num],r="u+cid",damage=0; - if(item.name=="placeholder") - return socket.emit("game_response",{response:"item_placeholder",place:data.name,failed:true}); - if(item.l) - return socket.emit("game_response",{response:"item_locked",place:data.name,failed:true}); - if(item.b) - return socket.emit("game_response",{response:"item_blocked",place:data.name,failed:true}); - var prop=calculate_item_properties(item),negative=false; - if(in_arr(item.name,G.skills.throw.negative)) negative=true; - G.skills.throw.nprop.forEach(function(p){if(prop[p]) negative=true;}); - if(negative && target.is_player && !is_in_pvp(player)) - return socket.emit("game_response",{response:"not_in_pvp",place:data.name,failed:true}); - consume_one(player,data.num); - if(item.name=="essenceoffire") add_condition(target,"eburn",{from:player}); - else if(item.name=="essenceoflife") add_condition(target,"eheal",{from:player}); - else if(prop.attack) damage=round(Math.random()*prop.attack*15); - else if(prop.armor) damage=round(Math.random()*prop.armor*24); - if(damage) - { - if(target["1hp"]) damage=1; - target.hp=max(1,target.hp-damage); - disappearing_text({},target,"-"+damage,{color:"red",xy:1}); + + if (to_update) { + resend(player, to_update); } - consume_mp(player,G.skills[data.name].mp,target); - xy_emit(player,"ui",{type:"throw",from:player.name,to:target.id,item:item.name}); - player.to_resend="u+cid+reopen"; - if(target.is_player) resend(target,r); - else target.u=true,target.cid++; - } - if(data.name=="phaseout") - { - if(!player.items[data.num] || player.items[data.num].name!="shadowstone") - return socket.emit("game_response",{response:"skill_cant_item",place:data.name,failed:true}); - consume_one(player,data.num); - consume_mp(player,G.skills[data.name].mp); - player.s.phasedout={ms:G.conditions.phasedout.duration}; - player.to_resend="u+cid+reopen"; - } - if(data.name=="pcoat") - { - if(!player.items[data.num] || player.items[data.num].name!="poison") - return socket.emit("game_response",{response:"skill_cant_item",place:data.name,failed:true}); - consume_one(player,data.num); - consume_mp(player,G.skills[data.name].mp); - player.s.poisonous={ms:G.skills.pcoat.cooldown}; - player.to_resend="u+cid+reopen"; - } - if(data.name=="curse") //#TODO: last_curse variable + check for multiple curses - { - var attack=commence_attack(player,target,"curse"); - if(!attack.failed) resolve=attack,add_pdps(player,null,player.attack/2); - else reject=attack,cool=false; - } - if(data.name=="snowball") - { - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - var found=false; - for(var i=player.isize-1;i>=0;i--) - { - if(player.items[i] && player.items[i].name=="snowball") - { - consume_one(player,i); - found=true; + success_response(resolve_type, resolve); + }); + socket.on("misc_npc", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var npc = players[data.npc]; + if (!npc || !npc.npc) { + return; + } + if (simple_distance(player, npc) > 1000) { + return socket.emit("game_response", "distance"); + } + if (data.npc == NPC_prefix + "Marven" && npc.misc == true) { + server_log("Here!"); + } + }); + socket.on("unequip", function (data) { + var player = players[socket.id]; + if (!player || !data.slot || !player.slots[data.slot]) { + return fail_response("invalid"); + } + if (data.slot == "elixir") { + return fail_response("cant"); + } + var item = player.slots[data.slot]; + var done = false; + if (in_arr(data.slot, trade_slots) && item.giveaway !== undefined) { + return fail_response("giveaway"); + } + // an oversight allowed .giveaway items to be equipped, so now they can be unequipped from regular slots [04/11/21] + if (player.esize <= 0 && !item.b) { + return fail_response("no_space"); + } + player.slots[data.slot] = null; + player.cslots[data.slot] = null; + player.c = {}; + if (!item.b) { + add_item(player, item, { announce: false }); + } + resend(player, "reopen+u+cid"); + success_response("data"); + }); + socket.on("secondhands", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (simple_distance(G.maps.main.ref.secondhands, player) > 500) { + return socket.emit("game_response", "distance"); + } + socket.emit("secondhands", csold); + }); + socket.on("lostandfound", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data == "info") { + return socket.emit("game_response", { response: "lostandfound_info", gold: S.gold }); + } + if (!player.donation) { + return socket.emit("game_response", "lostandfound_donate"); + } + if (simple_distance(G.maps.woffice.ref.lostandfound, player) > 500) { + return socket.emit("game_response", "distance"); + } + socket.emit("lostandfound", cfound); + }); + socket.on("split", function (data) { + var player = players[socket.id]; + var num = data.num; + var item = player.items[data.num]; + var quantity = min(max(parseInt(data.quantity) || 0, 1), (item && item.q) || 1); + if (!item) { + return fail_response("no_item"); + } + if (!G.items[item.name].s) { + return fail_response("invalid"); + } + quantity = min(quantity, G.items[item.name].s || 1); + if (!player.esize) { + return fail_response("cant_space"); + } + if (item.q == quantity) { + return success_response(); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + + consume(player, data.num, quantity); + + var new_item = cache_item(item); + if (item.q) { + new_item.q = quantity; + } + + for (var i = 0; i < player.isize; i++) { + if (!player.items[i]) { + player.items[i] = new_item; + player.citems[i] = cache_item(new_item); break; } } - if(!found) - return socket.emit("game_response",{response:"skill_cant_item",place:data.name,failed:true}); - var attack=commence_attack(player,target,"snowball"); - if(!attack.failed) resolve=attack; - else reject=attack,cool=false; - } - if(data.name=="entangle" || data.name=="tangle") - { - if(data.name=="entangle") - if(!player.items[data.num] || player.items[data.num].name!="essenceofnature") - return socket.emit("game_response",{response:"skill_cant_item",place:data.name,failed:true}); - if(target.s.invincible) - return socket.emit("game_response",{response:"target_invincible",place:data.name,failed:true}); - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - add_condition(target,"tangled",{ms:G.skills['entangle'].duration}); - target.abs=true; - target.moving=false; - xy_emit(player,"ui",{type:"entangle",from:player.name,to:target.id}); - consume_mp(player,G.skills[data.name].mp,target); - add_pdps(player,target,4000); - if(data.name=="entangle") consume_one(player,data.num); - player.to_resend="u+cid+reopen"; - if(target.is_monster) - { - target.u=true; target.cid++; ccms(target); + resend(player, "reopen"); + success_response(); + }); + socket.on("sell", function (data) { + var player = players[socket.id]; + var num = data.num; + var item = player.items[data.num]; + var quantity = min(max(parseInt(data.quantity) || 0, 1), (item && item.q) || 1); + var can_reach = false; + if (!player || player.user) { + return fail_response("cant_in_bank"); } - else - resend(target,"u+cid"); - } - if(data.name=="4fingers") - { - if(target.s.invincible) - return socket.emit("game_response",{response:"target_invincible",place:data.name,failed:true}); - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - if(!is_in_pvp(player) && !is_same(player,target,1)) - return socket.emit("game_response",{response:"non_friendly_target",place:data.name,failed:true}); - add_condition(target,"fingered",{ms:G.skills['4fingers'].duration}); - add_condition(target,"stunned",{duration:G.skills['4fingers'].duration-2000}); - xy_emit(player,"ui",{type:"4fingers",from:player.name,to:target.name}); - consume_mp(player,G.skills[data.name].mp,target); - add_pdps(player,target,1000); - resend(target,"u+cid"); - player.to_resend="u+cid"; - } - if(["quickpunch","quickstab","smash","mentalburst","purify","taunt","supershot","zapperzap","burst","piercingshot"].includes(data.name)) - { - var attack=commence_attack(player,target,data.name); - if(!attack.failed) resolve=attack; - else reject=attack,cool=false; - } - if(data.name=="poisonarrow") - { - if(!player.items[data.num] || player.items[data.num].name!="poison") - return socket.emit("game_response",{response:"skill_cant_item",place:data.name,failed:true}); - - var attack=commence_attack(player,target,data.name); - if(!attack.failed) resolve=attack; - else reject=attack,cool=false; - - consume_one(player,data.num); - player.to_resend="reopen"; - } - if(data.name=="revive") - { - if(!player.items[data.num] || player.items[data.num].name!="essenceoflife") - return socket.emit("game_response",{response:"skill_cant_item",place:data.name,failed:true}); - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - if(!target.rip) - return socket.emit("game_response",{response:"target_alive",place:data.name,failed:true}); - - consume_one(player,data.num); - player.to_resend="reopen"; - if(target.party==player.party) add_pdps(player,target,target.pdps/5); - - if(target.hp!=target.max_hp) - { - reject={response:"data",place:data.name,reason:"hp"} - player.socket.emit("game_response",{response:"revive_failed",id:data.id}); + if (!item) { + return fail_response("no_item"); } - else - { - target.c.revival={ms:8000,f:player.name}; + if (item.name == "placeholder") { + return fail_response("item_placeholder"); } - resend(target,"u+cid"); - } - if(data.name=="cburst") - { - var hit={},times=0,attack=null,c_resolve=null; - consume_mp(player,G.skills[data.name].mp); - player.first_burst=true; - player.halt=true; - if(is_array(data.targets)) data.targets.forEach(function(t){ - // console.log(id); - var id=t[0],mp=max(0,parseInt(t[1])||0); - if(player.mp<20 || times>16 || !mp) return; times+=1; - var target=instances[player.in].monsters[id]; if(!target) target=instances[player.in].players[id]; if(!target || is_invinc(target) || target.name==player.name) return; - if(hit[id]) return; - hit[id]=true; - player.next_mp=mp; - attack=commence_attack(player,target,"cburst"); - if(!attack || !attack.projectile) return; - if(!c_resolve) - { - c_resolve=attack; - attack.pids=[attack.pid]; - attack.targets=[attack.target]; - } - else - { - c_resolve.pids.push(attack.pid); - c_resolve.targets.push(attack.target); + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + (G.maps[player.map].merchants || []).forEach(function (m) { + if (simple_distance(player, m) < B.sell_dist) { + can_reach = m; } }); - player.halt=false; - player.to_resend="u+cid"; - if(!c_resolve) - { - if(attack) reject=attack; - disappearing_text(player.socket,player,"NO HITS"); + if (player.computer && !can_reach) { + can_reach = G.maps.main.merchants[0]; } - else resolve=c_resolve; - } - if(data.name=="partyheal") - { - var targets=[player],attack=null,hits=0; - if(parties[player.party]) - { - targets=[]; - parties[player.party].forEach(function(name){ - var current=players[name_to_id[name]]; - targets.push(current); + if (!can_reach) { + return fail_response("distance"); + } + can_reach.name = player.name; + var value = calculate_item_value(item); + consume(player, data.num, quantity); + player.gold += value * quantity; + var new_item = cache_item(item); + if (item.q) { + new_item.q = quantity; + } + xy_emit(can_reach, "ui", { + type: "-$", + id: can_reach.id, + name: player.name, + item: new_item, + num: num, + event: "sell", + }); + resend(player, "reopen+nc+inv"); + success_response("gold_received", { gold: value * quantity, item: new_item, cevent: "sell" }); + secondhands_logic(item, quantity); // In the end, so if this fails, the "sell" still succeeds - otherwise it could cause a infinite gold loophole [10/07/18] + }); + socket.on("buy_shells", function (data) { + return socket.emit("game_log", "No longer possible"); + var player = players[socket.id]; + if (!player || player.user || gameplay == "hardcore" || gameplay == "test") { + return game_response("cant_in_bank"); + } + var gold = parseInt(data.gold) || 0; + if (gold < 1000000 || gold > player.gold) { + return socket.emit("game_response", "not_enough_gold"); + } + var shells = Math.floor(gold / G.multipliers.shells_to_gold); + player.gold -= gold; + socket.emit("game_log", "Gave " + to_pretty_num(gold) + " gold"); + appengine_call( + "bill_user", + { auth: player.auth, amount: -shells, reason: "buy_shells", name: player.name }, + function (result) { + server_log("buy_with_cash: " + JSON.stringify(result)); + if (result.failed || !result.done) { + socket.emit("game_log", "Purchase failed"); + return; + } + player.cash = result.cash; + socket.emit("game_log", "Received " + to_pretty_num(shells) + " shells"); + + resend(player, "reopen+nc"); + }, + ); + resend(player, "reopen+nc"); + }); + socket.on("buy_with_cash", function (data) { + var player = players[socket.id]; + var name = data.name; + var def = G.items[data.name]; + var quantity = min(max(parseInt(data.quantity) || 0, 1), (def && def.s) || 9999); + if (!player || player.user || gameplay == "hardcore" || gameplay == "test") { + return fail_response("cant_in_bank"); + } + var cost = def.cash * quantity; + if (!def.cash || def.ignore) { + return fail_response("invalid"); + } + if (def.p2w) { + return fail_response("invalid"); + } + if (!def.s) { + quantity = 1; + } + if (!can_add_item(player, create_new_item(name, quantity))) { + return fail_response("no_space"); + } + appengine_call( + "bill_user", + { auth: player.auth, amount: cost, reason: data.name, name: player.name }, + function (result) { + server_log("buy_with_cash: " + JSON.stringify(result)); + if (result.failed || !result.done) { + socket.emit("game_log", "Purchase failed"); + return; + } + player.cash = result.cash; + var new_item = create_new_item(name, quantity); + var done = false; + add_item(player, new_item, { announce: false }); + socket.emit("game_log", "Spent " + to_pretty_num(cost) + " shells"); + + resend(player, "reopen+nc+inv"); + }, + ); + success_response({ success: false, in_progress: true }); + }); + socket.on("sbuy", function (data) { + var player = players[socket.id]; + var done = false; + var npc = G.maps.main.ref.secondhands; + var c = S.sold; + var cc = csold; + var e = "+$p"; + var ev = "secondhands"; + var mult = 2; + var src = "scnd"; + if (data.f) { + npc = G.maps.woffice.ref.lostandfound; + c = S.found; + cc = cfound; + e = "+$f"; + ev = "lostandfound"; + mult = 4; + src = "lost"; + } + if (!player || player.user) { + return game_response("cant_in_bank"); + } + if (player.s.hopsickness && ev == "lostandfound") { + return fail_response("cant_when_sick", { goblin: true }); + } + if (simple_distance(npc, player) > 500) { + return socket.emit("game_response", "distance"); + } + for (var i = 0; i < c.length; i++) { + if (c[i].rid == data.rid) { + if (mult == 2 && G.items[c[i].name].cash) { + mult = 3; + } + var gold = calculate_item_value(c[i]) * mult * (c[i].q || 1); + var item = c[i]; + if (!can_add_item(player, c[i])) { + return disappearing_text(socket, player, "NO SPACE"); + } + if (gold > player.gold) { + return socket.emit("game_response", "buy_cost"); + } + player.gold -= gold; + item.src = src; + add_item(player, c[i], { announce: false }); + c.splice(i, 1); + cc.splice(i, 1); + socket.emit("game_log", "Spent " + to_pretty_num(gold) + " gold"); + resend(player, "reopen+nc+inv"); + socket.emit(ev, cc); + done = true; + xy_emit(npc, "ui", { type: e, id: npc.id, name: player.name, item: cache_item(item, 1) }); + break; + } + } + if (!done) { + return socket.emit("game_log", "Item gone"); + } + }); + socket.on("buy", function (data) { + var player = players[socket.id]; + var can_reach = false; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + var name = data.name; + var quantity = min(max(parseInt(data.quantity) || 0, 1), (G.items[name] && G.items[name].s) || 9999); + var cost = 0; + var added = false; + var done = false; + if (!can_buy[name] && gameplay != "test") { + return fail_response("buy_cant_npc"); + } + if (!can_add_item(player, create_new_item(name, quantity))) { + return fail_response("buy_cant_space"); + } + (G.maps[player.map].items[name] || []).forEach(function (l) { + if (simple_distance(player, l) < B.sell_dist) { + can_reach = l; + } + }); + if (player.computer && !can_reach) { + can_reach = G.maps.main.merchants[0]; + } + if (!can_reach) { + return fail_response("distance"); + } + can_reach.name = player.name; + var def = G.items[name]; + if (!def.s) { + quantity = 1; + } + cost = quantity * G.items[name].g; + if (G.items[name].p2w) { + cost *= G.inflation; + } + if (player.gold < cost) { + return fail_response("buy_cost"); + } + var new_item = create_new_item(name, quantity); + player.gold -= cost; + var num = add_item(player, new_item, { announce: false }); + xy_emit(can_reach, "ui", { + type: "+$", + id: can_reach.id, + name: player.name, + item: cache_item(new_item), + event: "buy", + }); + + resend(player, "reopen+nc+inv"); + success_response("buy_success", { cost: cost, num: num, name: name, q: quantity, cevent: "buy" }); + }); + socket.on("send", function (data) { + var player = players[socket.id]; + var receiver = players[name_to_id[data.name || ""]]; + var num; + var s_item; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!receiver || receiver.user) { + return fail_response("receiver_unavailable"); + } + if (distance(receiver, player, true) > B.dist || receiver.map != player.map) { + return fail_response("distance"); + } + if (data.num !== undefined) { + data.num = max(0, parseInt(data.num) || 0); + var item = player.items[data.num]; + if (!item) { + return fail_response("send_no_item"); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l || (item.acl && receiver.owner != player.owner)) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + data.q = min(item.q || 1, max(1, parseInt(data.q || 1) || 1)); + if (!data.q) { + return fail_response("no_item"); + } + if (!can_add_item(receiver, create_new_item(item.name, data.q))) { + return fail_response("send_no_space"); + } + if ((item.q || 1) == data.q) { + player.items[data.num] = player.citems[data.num] = null; + player.esize++; + } else { + player.items[data.num].q -= data.q; + player.citems[data.num] = cache_item(player.items[data.num]); + } + + if (item.q) { + s_item = create_new_item(item.name, data.q); + if (item.v) { + s_item.v = item.v; + } + if (item.data) { + s_item.data = item.data; + } + num = add_item(receiver, s_item, { announce: false }); + } else { + s_item = item; + num = add_item(receiver, item, { announce: false }); + } + + if (receiver.owner != player.owner) { + item.src = "snd"; + } + + add_to_history(player, { name: "item", to: receiver.name, item: item.name, q: data.q, level: item.level }); + add_to_history(receiver, { name: "item", from: player.name, item: item.name, q: data.q, level: item.level }); + + xy_emit(player, "ui", { + type: "item_sent", + receiver: receiver.name, + sender: player.name, + item: cache_item(s_item, null, { q: data.q }), + num: num, + fnum: data.num, + event: true, + }); + + resend(player, "reopen+nc+inv"); + resend(receiver, "reopen+nc+inv"); + receiver.socket.emit("game_response", { + response: "item_received", + name: player.name, + item: item.name, + q: data.q, + num: num, + cevent: true, + }); + player.socket.emit("game_response", { + response: "item_sent", + name: receiver.name, + item: item.name, + q: data.q, + num: data.num, + cevent: true, + place: "send", + }); + } else if (data.gold !== undefined) { + data.gold = min(player.gold, max(1, parseInt(data.gold || 1) || 1)) || player.gold; + if (!data.gold) { + return fail_response("invalid"); + } + player.gold -= data.gold; + if (!is_same(player, receiver) && data.gold != 1) { + data.gold = parseInt(data.gold * 0.975); + } + receiver.gold += data.gold; + + add_to_history(player, { name: "gold", to: receiver.name, amount: data.gold }); + add_to_history(receiver, { name: "gold", from: player.name, amount: data.gold }); + + xy_emit(player, "ui", { + type: "gold_sent", + receiver: receiver.name, + sender: player.name, + gold: data.gold, + event: true, + }); + + resend(player, "reopen+nc+inv"); + resend(receiver, "reopen+nc+inv"); + receiver.socket.emit("game_response", { + response: "gold_received", + name: player.name, + gold: data.gold, + cevent: true, + }); + player.socket.emit("game_response", { + response: "gold_sent", + name: receiver.name, + gold: data.gold, + cevent: true, + place: "send", + }); + } else if (data.cx !== undefined) { + var cxl = map_cx(player); + var count = all_cx(player, 1); + if (!(cxl[data.cx] && player.p.acx[cxl[data.cx]] && count[data.cx] > 0)) { + return fail_response("send_no_cx"); + } + // count[cxl[data.cx]] before [28/01/22] + if (receiver.owner != player.owner) { + return fail_response("send_diff_owner"); + } + player.p.acx[cxl[data.cx]] -= 1; + if (!player.p.acx[cxl[data.cx]]) { + delete player.p.acx[cxl[data.cx]]; + } + receiver.p.acx[cxl[data.cx]] = (receiver.p.acx[cxl[data.cx]] || 0) + 1; + + xy_emit(player, "ui", { + type: "cx_sent", + receiver: receiver.name, + sender: player.name, + cx: data.cx, + event: true, + }); + + receiver.socket.emit("game_response", { + response: "cx_received", + name: player.name, + cx: data.cx, + acx: receiver.p.acx, + cevent: true, + }); + player.socket.emit("game_response", { + response: "cx_sent", + name: receiver.name, + cx: data.cx, + acx: player.p.acx, + cevent: true, + place: "send", }); } - targets.forEach(function(target){ - attack=commence_attack(player,target,"partyheal"); - if(!attack.failed) hits=true; + }); + socket.on("donate", function (data) { + var player = players[socket.id]; + var XPX = 3.2; + if (!player || player.user) { + return game_response("cant_in_bank"); + } + var gold = max(1, min(parseInt(data.gold) || 0, 1000000000)); + if (gold > player.gold) { + return socket.emit("game_response", "gold_not_enough"); + } + if (gold >= 1000000) { + response = "thx"; + player.donation = true; + } else if (gold < 100000) { + response = "low"; + } else { + add_item(player, "gum"); + response = "gum"; + } + if (S.gold < 500000000) { + XPX = 4.8; + } else if (S.gold <= 1000000000) { + XPX = 4; + } + player.gold -= gold; + S.gold += gold; + if (gold >= 5000000) { + lstack(S.logs.donate, { name: player.name, gold: gold, xp: XPX }); + } + if (player.type == "merchant") { + player.xp += parseInt(gold * XPX); + } + resend(player, "reopen"); + socket.emit("game_response", { response: "donate_" + response, gold: gold, xprate: XPX }); + }); + socket.on("destroy", function (data) { + var player = players[socket.id]; + var add = "+nc+inv"; + data.num = max(0, parseInt(data.num) || 0); + if (!player.items[data.num]) { + return fail_response("no_item"); + } + var item = player.items[data.num]; + var name = player.items[data.num].name; + data.q = min(max(parseInt(data.q) || 0, 1), (item && item.q) || 1); + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + if (item.level != 13) { + consume(player, data.num, data.q); + //player.items[data.num]=player.citems[data.num]=null; + } + if (data.statue) { + if (item.name == "shadowstone") { + add = "+u+cid"; + player.s.invis = { ms: 99999 }; + } + if (G.items[item.name].upgrade && Math.random() < 1.0 / ((gameplay == "hardcore" && 10000) || 1000000)) { + add = "+u+cid"; + item.level = 13; + player.items[data.num] = item; + player.citems[data.num] = cache_item(player.items[data.num]); + } + xy_emit(G.maps.spookytown.ref.poof, "upgrade", { type: "poof", success: 1 }); + } + resend(player, "reopen" + add); + success_response("destroyed", { name: name, place: "destroy", cevent: "destroy" }); + }); + socket.on("join_giveaway", function (data) { + var player = players[socket.id]; + var seller = players[id_to_id[data.id]]; + var num; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!in_arr(data.slot, trade_slots)) { + return fail_response("invalid"); + } + if (!seller || seller.npc || is_invis(seller)) { + return fail_response("seller_gone"); + } + if (seller.user) { + return fail_response("cant_in_bank"); + } + if (distance(seller, player, true) > B.dist || seller.map != player.map) { + return fail_response("distance"); + } + if (seller.id == player.id) { + return fail_response("hmm"); + } + var item = seller.slots[data.slot]; + if (!item || (data.rid && item.rid != data.rid)) { + return fail_response("item_gone"); + } + if (!item.giveaway) { + return fail_response("sneaky"); + } + if (!player.auth_id) { + return fail_response("need_auth"); + } + //if(item.list.includes(player.name)) return socket.emit("game_log","Already joined!"); + //if(player.type!="merchant") return socket.emit("game_log","Only merchants can join giveaways!"); + + //item.list.push(player.name); + + item.registry = item.registry || {}; + item.registry[player.auth_id] = player.name; + item.list = Object.values(item.registry); + + socket.emit("game_log", "Joined the giveaway!"); + seller.socket.emit("game_response", { + response: "giveaway_join", + name: player.name, + slot: data.slot, + cevent: true, }); - if(!hits) reject=attack; - } - if(data.name=="selfheal") - { - var attack=commence_attack(player,player,data.name); - if(!attack.failed) resolve=attack; - else reject=attack,cool=false; - } - if(data.name=="darkblessing" || data.name=="warcry") - { - consume_mp(player,G.skills[data.name].mp); - for(var id in instances[player.in].players) - { - var target=instances[player.in].players[id]; - if(!target.npc && distance(player,target,true) B.dist) { + return fail_response("distance"); + } + if (buyer.id == player.id) { + return fail_response("hmm"); + } + var item = buyer.slots[data.slot]; + if (!item || (data.rid && item.rid != data.rid)) { + return fail_response("item_gone"); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (!item.b && !B.rbugs) { + return fail_response("sneaky"); + } + if ((item.q || 1) < data.q) { + return fail_response("dont_have_enough"); + } + if (item.price * data.q > buyer.gold) { + return fail_response("buyer_gold"); + } + for (var i = 0; i < player.isize; i++) { + if ( + player.items[i] && + player.items[i].name == item.name && + (player.items[i].q || 1) >= data.q && + player.items[i].level == item.level && + !player.items[i].l + ) { + actual = player.items[i]; + num = i; + break; } } - xy_emit(player,"ui",{type:data.name}); - } - if(data.name=="3shot" || data.name=="5shot") - { - player.halt=true; - var times=0,hit={},reftarget=null,targets=3,attack=null,c_resolve=null; - if(data.name=="5shot") targets=5; - //console.log(data.ids); - if(is_array(data.ids)) - data.ids.forEach(function(id){ - if(times>=targets) return; times+=1; - var target=instances[player.in].monsters[id]; if(!target) target=instances[player.in].players[id]; - if(!target || is_invinc(target) || target.name==player.name) - { - attack={failed:true,place:data.name,reason:"no_target"}; + if (!actual) { + return fail_response("no_item"); + } + if (actual.b) { + return fail_response("item_blocked"); + } + if (!can_add_item(buyer, create_new_item(item.name, data.q))) { + return fail_response("trade_bspace"); + } + var price = item.price * data.q; + player.gold += round(price * (1 - player.tax)); + add_to_trade_history(player, "sell", buyer.name, cache_item(actual, true, { q: data.q }), price); + buyer.gold -= price; + add_to_trade_history(buyer, "buy", player.name, cache_item(actual, true, { q: data.q }), price); + S.gold += price - round(price * (1 - player.tax)); + + if ((item.q || 1) == data.q) { + buyer.slots[data.slot] = buyer.cslots[data.slot] = null; + } else { + buyer.slots[data.slot].q -= data.q; + buyer.cslots[data.slot] = cache_item(buyer.slots[data.slot], true); + } + + // up to here + + if ((player.items[i].q || 1) == data.q) { + player.items[num] = player.citems[num] = null; + } else { + player.items[num].q -= data.q; + player.citems[num] = cache_item(player.items[num]); + } + + if (G.items[item.name].s) { + bnum = add_item(buyer, create_new_item(item.name, data.q), { announce: false }); + } else { + bnum = add_item(buyer, actual, { announce: false }); + } + + if (player.type == "merchant") { + merchant_xp_logic(player, buyer, price, price - round(price * (1 - player.tax))); + } + if (buyer.type == "merchant") { + merchant_xp_logic(buyer, player, price, price - round(price * (1 - buyer.tax))); + } + + socket.emit( + "game_log", + "Sales tax " + to_pretty_num(price - round(price * (1 - player.tax))) + " gold [" + player.tax * 100 + "%]", + ); + socket.emit("game_log", "Received " + to_pretty_num(round(price * (1 - player.tax))) + " gold"); + buyer.socket.emit("game_log", "Spent " + to_pretty_num(price) + " gold"); + + xy_emit(buyer, "ui", { + type: "+$$", + seller: player.name, + buyer: buyer.name, + item: cache_item(actual, true, { q: data.q, price: item.price }), + slot: data.slot, + num: bnum, + snum: num, + }); + + resend(player, "reopen"); + resend(buyer, "reopen+u+cid"); + success_response({}); + }); + socket.on("trade_buy", function (data) { + var player = players[socket.id]; + var seller = players[id_to_id[data.id]]; + var num; + data.q = max(1, parseInt(data.q || 1) || 1); + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!in_arr(data.slot, trade_slots)) { + return fail_response("invalid"); + } + if (!seller || seller.npc || is_invis(seller)) { + return fail_response("seller_gone"); + } + if (seller.user) { + return fail_response("cant_in_bank"); + } + if (distance(seller, player, true) > B.dist || seller.map != player.map) { + return fail_response("distance"); + } + if (seller.id == player.id) { + return fail_response("hmm"); + } + var item = seller.slots[data.slot]; + if (!item || (data.rid && item.rid != data.rid)) { + return fail_response("item_gone"); + } + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.b || item.giveaway) { + return fail_response("sneaky"); + } + if (item.price * data.q > player.gold) { + return fail_response("gold_not_enough"); + } + if ((item.q || 1) < data.q) { + return fail_response("insufficient_q"); + } + if (!can_add_item(player, create_new_item(item.name, data.q))) { + return fail_response("no_space"); + } + var price = item.price * data.q; + player.gold -= price; + add_to_trade_history(player, "buy", seller.name, cache_item(seller.slots[data.slot], true, { q: data.q }), price); + seller.gold += round(price * (1 - seller.tax)); + add_to_trade_history( + seller, + "sell", + player.name, + cache_item(seller.slots[data.slot], true, { q: data.q }), + price, + ); + S.gold += price - round(price * (1 - seller.tax)); + + if ((item.q || 1) == data.q) { + seller.slots[data.slot] = seller.cslots[data.slot] = null; + } else { + seller.slots[data.slot].q -= data.q; + seller.cslots[data.slot] = cache_item(seller.slots[data.slot], true); + } + + if (item.q) { + num = add_item(player, create_new_item(item.name, data.q), { announce: false }); + } else { + num = add_item(player, item, { announce: false }); + } + + if (seller.owner != player.owner) { + item.src = "tb"; + } + + if (player.type == "merchant") { + merchant_xp_logic(player, seller, price, price - round(price * (1 - player.tax))); + } + if (seller.type == "merchant") { + merchant_xp_logic(seller, player, price, price - round(price * (1 - seller.tax))); + } + + socket.emit("game_log", "Spent " + to_pretty_num(price) + " gold"); + seller.socket.emit( + "game_log", + "Sales tax " + to_pretty_num(price - round(price * (1 - seller.tax))) + " gold [" + seller.tax * 100 + "%]", + ); + seller.socket.emit("game_log", "Received " + to_pretty_num(round(price * (1 - seller.tax))) + " gold"); + + xy_emit(seller, "ui", { + type: "+$$", + seller: seller.name, + buyer: player.name, + item: cache_item(item, true, { q: data.q }), + slot: data.slot, + num: data.num, + }); + + resend(player, "reopen"); + resend(seller, "reopen+u+cid"); + success_response({}); + }); + socket.on("trade_history", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + server_log("trade_history: " + player.name); + socket.emit("trade_history", player.p.trade_history || []); + }); + socket.on("merchant", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var initial = player.p.stand; + server_log("merchant: " + player.name); + if (data.close || player.p.stand) { + player.p.stand = false; + for (var i = 0; i < player.items.length; i++) { + if (player.items[i] && player.items[i].b == "stand") { + delete player.items[i].b; + } + } + } + if (data.num !== undefined) { + var item = player.items[data.num]; + if (item && G.items[item.name].stand) { + player.p.stand = G.items[item.name].stand; + item.b = "stand"; + } else { + return fail_response("invalid"); + } + } + if (initial != player.p.stand) { + // All unneccessary causes of resend's should be patched [03/08/18] + reslot_player(player); + resend(player, "u+cid"); + } + success_response({}); + }); + socket.on("imove", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + data.a = max(0, parseInt(data.a) || 0); + data.b = max(0, parseInt(data.b) || 0); + if (data.a == data.b) { + return fail_response("invalid"); + } + var a = min(data.a, data.b); + var b = max(data.a, data.b); + var a_item; + if (!(b < player.isize || b < player.items.length)) { + return fail_response("invalid"); + } + // while(b>=player.items.length) player.items.push(null); [22/11/16] + if (player.items[a] && player.items[a].name == "placeholder") { + return fail_response("item_placeholder"); + } + if (player.items[b] && player.items[b].name == "placeholder") { + return fail_response("item_placeholder"); + } + if (can_stack(player.items[a], player.items[b])) { + player.items[data.a].q = (player.items[a].q || 1) + (player.items[b].q || 1); + player.items[data.b] = null; + player.esize++; + } else { + a_item = player.items[a]; + player.items[a] = player.items[b]; + player.items[b] = a_item; + } + player.citems[data.a] = cache_item(player.items[data.a]); + player.citems[data.b] = cache_item(player.items[data.b]); + resend(player, "reopen+nc+inv"); + return success_response("data"); + }); + socket.on("bank", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!player.user || player.mounting || player.unmounting) { + return fail_response("bank_unavailable"); + } + var success = {}; + if (data.operation == "withdraw") { + var amount = max(0, min(parseInt(data.amount) || 0, player.user.gold)); + player.user.gold -= amount; + player.gold += amount; + success = { response: "bank_withdraw", gold: amount, cevent: true }; + } + if (data.operation == "deposit") { + var amount = max(0, min(parseInt(data.amount) || 0, player.gold)); + player.user.gold += amount; + player.gold -= amount; + success = { response: "bank_store", gold: amount, cevent: true }; + } + if (data.operation == "unlock") { + if (!bank_packs[data.pack]) { + return fail_response("invalid"); + } + var gold = bank_packs[data.pack][1]; + var shells = bank_packs[data.pack][2]; + if (!gold) { + return fail_response("gold_not_enough"); + } + if (player.user[data.pack]) { + return fail_response("invalid"); + } + if (data.gold) { + if (player.gold < gold) { + return fail_response("gold_not_enough"); + } + player.gold -= gold; + player.user[data.pack] = []; + player.cuser[data.pack] = []; + success = { response: "bank_new_pack", pack: data.pack, gold: gold, cevent: true }; + } else if (data.shells) { + if (player["unlocking_" + data.pack]) { + return fail_response("bank_opi"); + } + player["unlocking_" + data.pack] = true; + appengine_call( + "bill_user", + { + auth: player.auth, + amount: shells, + reason: data.pack, + name: player.name, + suffix: "/" + player.name + "/" + data.pack, + override: true, + }, + function (result) { + server_log("buy_with_cash: " + JSON.stringify(result)); + player["unlocking_" + data.pack] = false; + if (result.failed || !result.done) { + return socket.emit("game_log", "Purchase failed"); + } + player.cash = result.cash; + + player.user[data.pack] = []; + player.cuser[data.pack] = []; + game_response("bank_new_pack", { cevent: true, pack: data.pack, shells: shells }); + + resend(player, "reopen"); + }, + ); + success = { success: false, in_progress: true }; + } + } + if (data.operation == "move") { + //within a .itemsN + if (!player.user[data.pack] || bank_packs[data.pack][0] != player.map) { + return fail_response("invalid"); + } + server_log("storage move " + JSON.stringify(data)); + data.a = max(0, min(41, parseInt(data.a) || 0)); + data.b = max(0, min(41, parseInt(data.b) || 0)); + if (data.a == data.b) { + return fail_response("invalid"); + } + if (player.user[data.pack][data.a] && player.user[data.pack][data.a].name == "placeholder") { + return fail_response("item_placeholder"); + } + if (player.user[data.pack][data.b] && player.user[data.pack][data.b].name == "placeholder") { + return fail_response("item_placeholder"); + } + if (can_stack(player.user[data.pack][data.a], player.user[data.pack][data.b])) { + player.user[data.pack][data.b].q = + (player.user[data.pack][data.a].q || 1) + (player.user[data.pack][data.b].q || 1); + player.user[data.pack][data.a] = null; + } else { + var temp = player.user[data.pack][data.a]; + player.user[data.pack][data.a] = player.user[data.pack][data.b]; + player.user[data.pack][data.b] = temp; + } + player.cuser[data.pack][data.a] = cache_item(player.user[data.pack][data.a]); + player.cuser[data.pack][data.b] = cache_item(player.user[data.pack][data.b]); + } + if (data.operation == "swap") { + //between .items and a .itemsN + var operation = "swap"; + if (!player.user[data.pack] || bank_packs[data.pack][0] != player.map) { + return fail_response("invalid"); + } + data.str = parseInt(data.str); + data.inv = parseInt(data.inv); + if (data.inv == -1 || (!data.inv && data.inv !== 0)) { + operation = "pull"; + for (var i = 0; i < player.isize; i++) { + if (!player.items[i]) { + data.inv = i; + break; + } + } + // if(data.inv==-1) { socket.emit("game_log","Inventory is full"); return; } + } + if (data.str == -1 || (!data.str && data.str !== 0)) { + if (operation == "pull") { + return fail_response("invalid"); + } + operation = "store"; + for (var i = 0; i < 42; i++) { + if (!player.user[data.pack][i]) { + data.str = i; + break; + } + } + // if(data.str==-1) { socket.emit("game_log","Storage is full"); return; } + } + server_log("storage swap " + JSON.stringify(data)); + data.str = max(0, min(41, parseInt(data.str) || 0)); + data.inv = max(0, min(player.isize - 1, parseInt(data.inv) || 0)); + var bank_item = player.user[data.pack][data.str]; + var inv_item = player.items[data.inv]; + if (inv_item && inv_item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (inv_item && inv_item.b) { + return fail_response("item_blocked"); + } + if (inv_item) { + delete inv_item.m; + delete inv_item.v; + } + if (operation == "swap") { + player.user[data.pack][data.str] = inv_item; + player.items[data.inv] = bank_item; + player.cuser[data.pack][data.str] = cache_item(player.user[data.pack][data.str]); + player.citems[data.inv] = cache_item(player.items[data.inv]); + } else if (operation == "store" && inv_item) { + if (!can_add_item(player.user[data.pack], inv_item)) { + return fail_response("storage_full"); + } + player.items[data.inv] = player.citems[data.inv] = null; + bank_add_item(player, data.pack, inv_item); + } else if (operation == "pull" && bank_item) { + if (!can_add_item(player, bank_item)) { + return fail_response("inventory_full"); + } + player.user[data.pack][data.str] = player.cuser[data.pack][data.str] = null; + add_item(player, bank_item, { announce: false }); + } + } + if (!player.user.gold && player.user.gold !== 0) { + player.user.gold = 0; + server_log("#X - GOLD BUG bank", 1); + } + resend(player, "reopen"); + success_response(success); + }); + socket.on("throw", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var item = player.items[data.num]; + if (item) { + if (item.name == "placeholder") { + return fail_response("item_placeholder"); + } + if (item.l) { + return fail_response("item_locked"); + } + if (item.b) { + return fail_response("item_blocked"); + } + var def = G.items[item.name]; + if (!def.throw) { + return fail_response("invalid"); + } + var x = parseFloat(data.x) || 0; + var y = parseFloat(data.y) || 0; + if (distance(player, { map: player.map, in: player.in, x: x, y: y }) > player.str * 3) { + fail_response("too_far"); + } + consume_one(player, data.num); + if (item.name == "confetti") { + player.thrilling = future_s(20); + xy_emit( + { map: player.map, in: player.in, x: x, y: y }, + "eval", + "confetti_shower({in:'" + player.in + "',map:'" + player.map + "',real_x:" + x + ",real_y:" + y + "})", + ); + } + if (item.name == "firecrackers") { + player.thrilling = future_s(200); + xy_emit( + { map: player.map, in: player.in, x: x, y: y }, + "eval", + "firecrackers({in:'" + player.in + "',map:'" + player.map + "',real_x:" + x + ",real_y:" + y + "})", + ); + for (var id in instances[player.in].monsters) { + var monster = instances[player.in].monsters[id]; + if (monster.target && distance({ map: player.map, in: player.in, x: x, y: y }, monster) < 64) { + stop_pursuit(monster, { force: true, cause: "firecrackers" }); + } + } + } + if (item.name == "whiteegg") { + xy_emit({ map: player.map, in: player.in, x: x, y: y }, "eval", "egg_splash(" + x + "," + y + ")"); + for (var id in instances[player.in].monsters) { + var monster = instances[player.in].monsters[id]; + if (!monster.target && distance({ map: player.map, in: player.in, x: x, y: y }, monster) < 32) { + target_player(monster, player); + } + } + } + if (item.name == "smoke") { + xy_emit({ map: player.map, in: player.in, x: x, y: y }, "eval", "assassin_smoke(" + x + "," + y + ");"); + } + resend(player, "reopen+nc+inv"); + success_response({}); + } else { + fail_response("no_item"); + } + }); + socket.on("poke", function (data) { + var player = players[socket.id]; + var level = 1; + if (!player || !player.slots.gloves || player.slots.gloves.name != "poker") { + return; + } + if (player.pokes >= 50) { + return socket.emit("game_log", "You are out of pokes!"); + } + player.pokes = (player.pokes || 0) + 1; + if (player.slots.gloves.level >= 10) { + level = 4; + } else if (player.slots.gloves.level == 9) { + level = 3; + } else if (player.slots.gloves.level == 8) { + level = 2; + } + xy_emit(player, "poke", { name: data.name, level: level, who: player.name }); + }); + socket.on("merge", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var container = player.slots[data.container]; + var pet = player.slots[data.pet]; + if (!container || container.type != "container" || !pet || pet.type != "container") { + return socket.emit("game_response", "merge_mismatch"); + } + if (G.items[container.name].grade < G.items[pet.name].grade || container.data) { + return socket.emit("game_response", "merge_mismatch"); + } + container.data = G.items[pet.name].pet; + player.slots[data.pet] = null; + socket.emit("game_response", "merge_complete"); + resend(player, "reopen"); + }); + socket.on("activate", function (data) { + var player = players[socket.id]; + // console.log(JSON.stringify(data)); + if (!player) { + return; + } + if (data.slot) { + var item = player.slots[data.slot]; + if (!item || in_arr(data.slot, trade_slots)) { return; } - if(hit[id]) return; - hit[id]=true; - attack=commence_attack(player,target,data.name); - if(!attack || !attack.projectile) return; - if(!c_resolve) - { - reftarget=target; - c_resolve=attack; - attack.pids=[attack.pid]; - attack.targets=[attack.target]; - } - else - { - c_resolve.pids.push(attack.pid); - c_resolve.targets.push(attack.target); - } - // if(times==1 && attack==null) times=40; - }); - consume_mp(player,G.skills[data.name].mp,reftarget); - player.halt=false; - player.to_resend="u+cid"; - if(!c_resolve) - { - if(attack) reject=attack; - disappearing_text(player.socket,player,"NO HITS"); - } - else resolve=c_resolve; - } - if(data.name=="track") - { - var list=[]; - for(var id in instances[player.in].players) - { - if(id==player.name) continue; - var target=instances[player.in].players[id],current={sound:"wmp"}; if(target.npc) continue; - if(is_invis(target)) current.invis=true; - if(target.type=="rogue" || target.type=="ranger") current.sound="rr"; - else if(target.type=="priest" || target.type=="mage") current.sound="pm"; - current.dist=distance(player,target); - if(current.distG.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - var ids=[]; - if(is_same(player,target,1)) - { - consume_mp(player,G.skills[data.name].mp); - } - else - { - if(player.mp= 8 && (player.type == "mage" || player.type == "priest")) { + player.tskin = "snow_angel"; + } else { + return socket.emit("game_response", "nothing"); + } + } else if (item.name == "tristone") { + var on = true; + if (player.tskin || player.tactivations == 100) { + player.tskin = ""; + on = false; + } else if (item.level <= 1 && deduct_gender(player) == "female") { + player.tskin = random_one(["tf_green", "tf_pink"]); + } else if (item.level == 2 && deduct_gender(player) == "female") { + player.tskin = random_one(["tf_blue", "tf_purple"]); + } else if (deduct_gender(player) == "female") { + player.tskin = "tf_orange"; + } else if (item.level <= 1) { + player.tskin = random_one(["tm_gray", "tm_brown", "tm_white"]); + } else if (item.level == 2) { + player.tskin = random_one(["tm_green", "tm_yellow", "tm_purple"]); + } else { + player.tskin = random_one(["tm_blue", "tm_red"]); + } + player.tactivations = (player.tactivations || 0) + 1; + } else if (item.name == "darktristone") { + var on = true; + if (player.tskin || player.tactivations == 100) { + player.tskin = ""; + on = false; + } else if (item.level == 4 && deduct_gender(player) == "female") { + player.tskin = "mf_blue"; + } else if (deduct_gender(player) == "female") { + player.tskin = "mf_yellow"; + } else if (item.level == 4) { + player.tskin = "mm_blue"; + } else { + player.tskin = "mm_yellow"; + } + player.tactivations = (player.tactivations || 0) + 1; + } else { + return; + } + resend(player, "reopen+nc+inv+u+cid"); + } else { + var item = player.items[data.num]; + if (item) { + if (item.name == "frozenstone") { + consume_one(player, data.num); + } + if (item.name == "bkey") { + if (!player.user) { + return socket.emit("game_response", "only_in_bank"); + } + if (player.user.unlocked && player.user.unlocked.bank_b) { + return socket.emit("game_response", "already_unlocked"); + } + consume_one(player, data.num); + player.user.unlocked = player.user.unlocked || {}; + player.user.unlocked.bank_b = new Date(); + player.user.items8 = player.user.items8 || []; + socket.emit("game_response", "door_unlocked"); + } + if (item.name == "ukey") { + if (!player.user) { + return socket.emit("game_response", "only_in_bank"); + } + if (player.user.unlocked && player.user.unlocked.bank_u) { + return socket.emit("game_response", "already_unlocked"); + } + consume_one(player, data.num); + player.user.unlocked = player.user.unlocked || {}; + player.user.unlocked.bank_u = new Date(); + player.user.items24 = player.user.items24 || []; + socket.emit("game_response", "door_unlocked"); + } + if (item.name == "dkey") { + if (!player.user) { + return socket.emit("game_response", "only_in_bank"); + } + var found = false; + for (var i = 0; i < 48; i++) { + var pack = "items" + i; + if (!bank_packs[pack] || player.user[pack]) { + continue; + } + found = true; + player.user[pack] = []; + player.cuser[pack] = []; + break; + } + if (!found) { + return; + } + consume_one(player, data.num); + socket.emit("game_response", "bank_pack_unlocked"); + } + } + resend(player, "reopen+nc+inv"); + } + }); + socket.on("booster", function (data) { + var player = players[socket.id]; + var item = player.items[data.num]; + if (!player) { + return; + } + server_log("booster " + data.num + " " + data.action); + if (!item || !in_arr(item.name, booster_items)) { + return fail_response("invalid"); + } + if (data.action == "activate" && !item.expires) { + item.expires = new Date(); + item.expires.setDate(item.expires.getDate() + 30 + (item.level || 0) * 2); + //item.expires.setMinutes(item.expires.getMinutes()+2); + } else if (data.action == "shift") { + if (!in_arr(data.to, booster_items)) { + return fail_response("invalid"); + } + player.xpm = player.goldm = player.luckm = 1; + item.name = data.to; + player.s.penalty_cd = { ms: min(((player.s.penalty_cd && player.s.penalty_cd.ms) || 0) + 240, 120000) }; + } + // if(item.expires) item.p="legacy"; + player.citems[data.num] = cache_item(player.items[data.num]); + resend(player, "reopen"); + success_response({ name: player.items[data.num].name }); + }); + socket.on("convert", function (data) { + // There are a lot of routines that could give birth to an endgame bug + // This routine ended up being the one + // "stone" boosters were originally 1200 shells + // when they were discontinued, I let players exchange them for 3600 shells instead + // turns out 'buy_with_cash' didn't check whether an item has "ignore" or not + // so one could buy the stones for 1200 shells and sell them for 3600 + // unlocking infinite gold and shells [17/01/18] + var player = players[socket.id]; + var item = player.items[data.num]; + var amount = 3600; + server_log("stone " + data.num + " " + data.action); + if (!item || !in_arr(item.name, ["stoneofxp", "stoneofgold", "stoneofluck"])) { + return; + } + if (item.expires) { + amount = round(600 - (hsince(item.expires) * 100) / 24.0); + } + add_shells(player, amount, "convert"); + player.items[data.num] = null; + player.citems[data.num] = cache_item(player.items[data.num]); + resend(player, "reopen+cid"); + }); + socket.on("emotion", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!data.name) { + data.name = random_one(Object.keys(player.p.emx)); + } + if (player.last.emotion && mssince(player.last.emotion) < 2000) { + return socket.emit("game_response", "emotion_cooldown"); + } + player.last.emotion = new Date(); + if (!G.emotions[data.name] || !player.p.emx[data.name]) { + return socket.emit("game_response", "emotion_cant"); + } + xy_emit(player, "emotion", { name: data.name, player: player.name }); + }); + socket.on("skill", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var target = null; + var cool = true; + var resolve = { response: "data", place: data.name, success: true }; + var reject = null; + player.first = true; + G.skills.attack.cooldown = player.attack_ms; + server_log("skill " + JSON.stringify(data)); + if (is_disabled(player)) { + return fail_response("disabled", data.name); + } + if ((player.tskin == "konami" || is_silenced(player)) && data.name != "attack") { + return socket.emit("game_response", { response: "skill_cant_incapacitated", place: data.name, failed: true }); + } + if (!G.skills[data.name]) { + return socket.emit("game_response", { response: "no_skill", place: data.name, failed: true }); + } + if ( + G.skills[data.name].mp && + player.mp < (G.skills[data.name].mp * (100 - (player.mp_reduction || 0))) / 100.0 && + !(player.role == "gm" && data.name == "blink") + ) { + return socket.emit("game_response", { response: "no_mp", place: data.name, failed: true }); + } + if (G.skills[data.name].level && player.level < G.skills[data.name].level) { + return socket.emit("game_response", { response: "no_level", place: data.name, failed: true }); + } + if ( + (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) && + player.last[data.name] && + mssince(player.last[data.name]) < (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) + ) { + return socket.emit("game_response", { + response: "cooldown", + skill: data.name, + place: data.name, + id: data.id, + ms: (G.skills[data.name].cooldown || G.skills[data.name].reuse_cooldown) - mssince(player.last[data.name]), + failed: true, + }); + } + if ( + G.skills[data.name].share && + player.last[G.skills[data.name].share] && + mssince(player.last[G.skills[data.name].share]) < + G.skills[G.skills[data.name].share].cooldown * (G.skills[data.name].cooldown_multiplier || 1) + ) { + return socket.emit("game_response", { + response: "cooldown", + skill: data.name, + place: data.name, + failed: true, + id: data.id, + ms: + G.skills[G.skills[data.name].share].cooldown * (G.skills[data.name].cooldown_multiplier || 1) - + mssince(player.last[G.skills[data.name].share]), + }); + } + if (G.skills[data.name].class && !in_arr(player.type, G.skills[data.name].class) && player.role != "gm") { + return socket.emit("game_response", { response: "skill_cant_use", place: data.name, failed: true }); + } + if ( + G.skills[data.name].wtype && + !is_array(G.skills[data.name].wtype) && + (!player.slots.mainhand || G.items[player.slots.mainhand.name].wtype != G.skills[data.name].wtype) && + player.role != "gm" + ) { + return socket.emit("game_response", { response: "skill_cant_wtype", place: data.name, failed: true }); + } + if ( + is_array(G.skills[data.name].wtype) && + (!player.slots.mainhand || !in_arr(G.items[player.slots.mainhand.name].wtype, G.skills[data.name].wtype)) && + player.role != "gm" + ) { + return socket.emit("game_response", { response: "skill_cant_wtype", place: data.name, failed: true }); + } + if (G.skills[data.name].hostile && G.maps[player.map].safe) { + return socket.emit("game_response", { response: "skill_cant_safe", place: data.name, failed: true }); + } + if (G.skills[data.name].target) { + if ( + ("" + parseInt(data.id) === "" + data.id && G.skills[data.name].target == "player") || + ("" + parseInt(data.id) !== "" + data.id && G.skills[data.name].target == "monster") + ) { + return fail_response("invalid_target", data.name, { id: data.id }); + } + if (G.skills[data.name].target != "monster" && players[id_to_id[data.id]]) { + target = players[id_to_id[data.id]]; + if (G.skills[data.name].hostile && target.name == player.name) { + return socket.emit("game_response", { response: "no_target", place: data.name, failed: true }); + } + } else if (G.skills[data.name].target != "player" && instances[player.in].monsters[data.id]) { + target = instances[player.in].monsters[data.id]; + } else { + return socket.emit("disappear", { id: data.id, place: data.name, reason: "not_there" }); + } + + if ( + is_invis(target) || + (!G.skills[data.name].global && (target.map != player.map || distance(target, player, true) > B.max_vision)) + ) { + return socket.emit("disappear", { id: data.id, place: data.name, reason: "not_there" }); + } + } + if (G.skills[data.name].requirements) { + for (var requirement in G.skills[data.name].requirements) { + if (!player[requirement] || player[requirement] < G.skills[data.name].requirements[requirement]) { + return socket.emit("game_response", { + response: "skill_cant_requirements", + place: data.name, + failed: true, + }); + } + } + } + if (G.skills[data.name].slot) { + var found = false; + var charges = false; + G.skills[data.name].slot.forEach(function (p) { + if (player.slots[p[0]] && player.slots[p[0]].name == p[1]) { + if (G.items[player.slots[p[0]].name].charge) { + if ((player.slots[p[0]].charges || 0) < G.items[player.slots[p[0]].name].charge) { + charges = true; + return; + } + player.slots[p[0]].charges -= G.items[player.slots[p[0]].name].charge; + player.cslots[p[0]] = cache_item(player.slots[p[0]]); + } + found = true; + } + }); + if (charges) { + return socket.emit("game_response", { response: "skill_cant_charges", place: data.name, failed: true }); + } + if (!found) { + return socket.emit("game_response", { response: "skill_cant_slot", place: data.name, failed: true }); + } + } + if (player.s.invincible) { + delete player.s.invincible; + player.to_resend = "u+cid"; + } + if (data.name == "attack" || data.name == "heal") { + var attack = commence_attack(player, target, data.name); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + } + if (data.name == "invis" && !player.s.invis) { + if (player.s.marked) { + return socket.emit("game_response", { response: "skill_cant_use", place: data.name, failed: true }); + } + player.s.invis = { ms: 999999999999999 }; + xy_emit(player, "disappear", { id: player.id, invis: true, reason: "invis" }); + player.to_resend = " "; + } + if (data.name == "pickpocket") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + + consume_mp(player, G.skills[data.name].mp); + player.c[data.name] = { + ms: + G.skills[data.name].duration_min + + Math.random() * (G.skills[data.name].duration_max - G.skills[data.name].duration_min), + target: target.name, + }; + player.to_resend = "u+cid"; + resolve = { response: "data", place: data.name, success: false, in_progress: true }; + } + if (data.name == "fishing" || data.name == "mining") { + var direction = 0; + var the_zone = null; + consume_mp(player, G.skills[data.name].mp); + (G.maps[player.map].zones || []).forEach(function (zone) { + if (zone.type != data.name || the_zone) { + return; + } + [ + [0, -24, 3], + [-24, 0, 1], + [24, 0, 2], + [0, 24, 0], + ].forEach(function (m) { + if (is_point_inside([player.x + m[0], player.y + m[1]], zone.polygon)) { + the_zone = zone; + direction = m[2]; + } + }); + }); + if (!the_zone || player.moving) { + xy_emit(player, "ui", { type: data.name + "_fail", name: player.name }); + reject = { response: "data", place: data.name }; + } else { + xy_emit(player, "ui", { type: data.name + "_start", name: player.name, direction: direction }); + player.c[data.name] = { + ms: + G.skills[data.name].duration_min + + Math.random() * (G.skills[data.name].duration_max - G.skills[data.name].duration_min), + drop: the_zone.drop, + }; + } + resolve = { response: "data", place: data.name, success: false, in_progress: true }; + player.to_resend = "u+cid"; + } + if (data.name == "light") { + consume_mp(player, G.skills[data.name].mp); + xy_emit(player, "light", { name: player.name }); + player.to_resend = "u+cid"; + } + if (data.name == "charge") { + player.s.charging = { ms: G.skills.charge.duration }; + player.to_resend = "u+cid"; + } + if (data.name == "dash") { + //console.log(data); + consume_mp(player, G.skills[data.name].mp); + var x = parseFloat(data.x) || 0; + var y = parseFloat(data.y) || 0; + var point = true; + if (point_distance(player.x, player.y, x, y) > 50) { + point = false; + player.socket.emit("game_response", "dash_failed"); + reject = { response: "data", place: data.name }; + } + if (point) { + var spot = safe_xy_nearby(player.map, x, y); + if (!spot) { + spot = safe_xy_nearby(player.map, player.x + (x - player.x) * 0.8, player.y + (y - player.y) * 0.8); + } + if (!spot) { + spot = safe_xy_nearby(player.map, player.x + (x - player.x) * 0.6, player.y + (y - player.y) * 0.6); + } + if (!spot || point_distance(player.x, player.y, spot.x, spot.y) < 10) { + point = false; + player.socket.emit("game_response", "dash_failed"); + reject = { response: "data", place: data.name }; + } + if (point) { + //console.log([player.x,player.y,spot.x,spot.y]); + player.s.dash = { ms: 1000 }; + player.speed = 500; + player.going_x = spot.x; + player.going_y = spot.y; + start_moving_element(player); + player.to_resend = "u+cid"; + player.socket.emit("eval", { code: "ui_move(" + spot.x + "," + spot.y + ")" }); + } + } + } + if (data.name == "hardshell" || data.name == "power" || data.name == "xpower") { + consume_mp(player, G.skills[data.name].mp); + player.s[G.skills[data.name].condition] = { + ms: G.skills[data.name].duration || G.conditions[G.skills[data.name].condition].duration, + }; + player.to_resend = "u+cid"; + } + if (data.name == "mshield") { + consume_mp(player, G.skills[data.name].mp); + if (player.s[G.skills[data.name].condition]) { + delete player.s[G.skills[data.name].condition]; + } else { + player.s[G.skills[data.name].condition] = { ms: 999999999 }; + } + player.to_resend = "u+cid"; + } + if ( + data.name == "mcourage" || + data.name == "mfrenzy" || + data.name == "massproduction" || + data.name == "massproductionpp" + ) { + consume_mp(player, G.skills[data.name].mp); + player.s[G.skills[data.name].condition] = { ms: G.conditions[G.skills[data.name].condition].duration }; + xy_emit(player, "ui", { type: data.name, name: player.name }); + player.to_resend = "u+cid"; + } + if (data.name == "throw") { + if (distance(player, target, true) > G.skills[data.name].range + player.level) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + if (target.s.invincible || target.immune) { + return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); + } + if (!player.items[data.num]) { + return socket.emit("game_response", { response: "skill_no_item", place: data.name, failed: true }); + } + var item = player.items[data.num]; + var r = "u+cid"; + var damage = 0; + if (item.name == "placeholder") { + return socket.emit("game_response", { response: "item_placeholder", place: data.name, failed: true }); + } + if (item.l) { + return socket.emit("game_response", { response: "item_locked", place: data.name, failed: true }); + } + if (item.b) { + return socket.emit("game_response", { response: "item_blocked", place: data.name, failed: true }); + } + var prop = calculate_item_properties(item); + var negative = false; + if (in_arr(item.name, G.skills.throw.negative)) { + negative = true; + } + G.skills.throw.nprop.forEach(function (p) { + if (prop[p]) { + negative = true; + } + }); + if (negative && target.is_player && !is_in_pvp(player)) { + return socket.emit("game_response", { response: "not_in_pvp", place: data.name, failed: true }); + } + consume_one(player, data.num); + if (item.name == "essenceoffire") { + add_condition(target, "eburn", { from: player }); + } else if (item.name == "essenceoflife") { + add_condition(target, "eheal", { from: player }); + } else if (prop.attack) { + damage = round(Math.random() * prop.attack * 15); + } else if (prop.armor) { + damage = round(Math.random() * prop.armor * 24); + } + if (damage) { + if (target["1hp"]) { + damage = 1; + } + target.hp = max(1, target.hp - damage); + disappearing_text({}, target, "-" + damage, { color: "red", xy: 1 }); + } + consume_mp(player, G.skills[data.name].mp, target); + xy_emit(player, "ui", { type: "throw", from: player.name, to: target.id, item: item.name }); + player.to_resend = "u+cid+reopen"; + if (target.is_player) { + resend(target, r); + } else { + target.u = true; + target.cid++; + } + } + if (data.name == "phaseout") { + if (!player.items[data.num] || player.items[data.num].name != "shadowstone") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + consume_one(player, data.num); + consume_mp(player, G.skills[data.name].mp); + player.s.phasedout = { ms: G.conditions.phasedout.duration }; + player.to_resend = "u+cid+reopen"; + } + if (data.name == "pcoat") { + if (!player.items[data.num] || player.items[data.num].name != "poison") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + consume_one(player, data.num); + consume_mp(player, G.skills[data.name].mp); + player.s.poisonous = { ms: G.skills.pcoat.cooldown }; + player.to_resend = "u+cid+reopen"; + } + if (data.name == "curse") { + //#TODO: last_curse variable + check for multiple curses + var attack = commence_attack(player, target, "curse"); + if (!attack.failed) { + resolve = attack; + add_pdps(player, null, player.attack / 2); + } else { + reject = attack; + cool = false; + } + } + if (data.name == "snowball") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + var found = false; + for (var i = player.isize - 1; i >= 0; i--) { + if (player.items[i] && player.items[i].name == "snowball") { + consume_one(player, i); + found = true; + break; + } + } + if (!found) { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + var attack = commence_attack(player, target, "snowball"); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + } + if (data.name == "entangle" || data.name == "tangle") { + if (data.name == "entangle") { + if (!player.items[data.num] || player.items[data.num].name != "essenceofnature") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + } + if (target.s.invincible) { + return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); + } + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + add_condition(target, "tangled", { ms: G.skills["entangle"].duration }); + target.abs = true; + target.moving = false; + xy_emit(player, "ui", { type: "entangle", from: player.name, to: target.id }); + consume_mp(player, G.skills[data.name].mp, target); + add_pdps(player, target, 4000); + if (data.name == "entangle") { + consume_one(player, data.num); + } + player.to_resend = "u+cid+reopen"; + if (target.is_monster) { + target.u = true; + target.cid++; + ccms(target); + } else { + resend(target, "u+cid"); + } + } + if (data.name == "4fingers") { + if (target.s.invincible) { + return socket.emit("game_response", { response: "target_invincible", place: data.name, failed: true }); + } + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + if (!is_in_pvp(player) && !is_same(player, target, 1)) { + return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); + } + add_condition(target, "fingered", { ms: G.skills["4fingers"].duration }); + add_condition(target, "stunned", { duration: G.skills["4fingers"].duration - 2000 }); + xy_emit(player, "ui", { type: "4fingers", from: player.name, to: target.name }); + consume_mp(player, G.skills[data.name].mp, target); + add_pdps(player, target, 1000); + resend(target, "u+cid"); + player.to_resend = "u+cid"; + } + if ( + [ + "quickpunch", + "quickstab", + "smash", + "mentalburst", + "purify", + "taunt", + "supershot", + "zapperzap", + "burst", + "piercingshot", + ].includes(data.name) + ) { + var attack = commence_attack(player, target, data.name); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + } + if (data.name == "poisonarrow") { + if (!player.items[data.num] || player.items[data.num].name != "poison") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + + var attack = commence_attack(player, target, data.name); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + + consume_one(player, data.num); + player.to_resend = "reopen"; + } + if (data.name == "revive") { + if (!player.items[data.num] || player.items[data.num].name != "essenceoflife") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + if (!target.rip) { + return socket.emit("game_response", { response: "target_alive", place: data.name, failed: true }); + } + + consume_one(player, data.num); + player.to_resend = "reopen"; + if (target.party == player.party) { + add_pdps(player, target, target.pdps / 5); + } + + if (target.hp != target.max_hp) { + reject = { response: "data", place: data.name, reason: "hp" }; + player.socket.emit("game_response", { response: "revive_failed", id: data.id }); + } else { + target.c.revival = { ms: 8000, f: player.name }; + } + resend(target, "u+cid"); + } + if (data.name == "cburst") { + var hit = {}; + var times = 0; + var attack = null; + var c_resolve = null; + consume_mp(player, G.skills[data.name].mp); + player.first_burst = true; + player.halt = true; + if (is_array(data.targets)) { + data.targets.forEach(function (t) { + // console.log(id); + var id = t[0]; + var mp = max(0, parseInt(t[1]) || 0); + if (player.mp < 20 || times > 16 || !mp) { + return; + } + times += 1; + var target = instances[player.in].monsters[id]; + if (!target) { + target = instances[player.in].players[id]; + } + if (!target || is_invinc(target) || target.name == player.name) { + return; + } + if (hit[id]) { + return; + } + hit[id] = true; + player.next_mp = mp; + attack = commence_attack(player, target, "cburst"); + if (!attack || !attack.projectile) { + return; + } + if (!c_resolve) { + c_resolve = attack; + attack.pids = [attack.pid]; + attack.targets = [attack.target]; + } else { + c_resolve.pids.push(attack.pid); + c_resolve.targets.push(attack.target); + } + }); + } + player.halt = false; + player.to_resend = "u+cid"; + if (!c_resolve) { + if (attack) { + reject = attack; + } + disappearing_text(player.socket, player, "NO HITS"); + } else { + resolve = c_resolve; + } + } + if (data.name == "partyheal") { + var targets = [player]; + var attack = null; + var hits = 0; + if (parties[player.party]) { + targets = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + targets.push(current); + }); + } + targets.forEach(function (target) { + attack = commence_attack(player, target, "partyheal"); + if (!attack.failed) { + hits = true; + } + }); + if (!hits) { + reject = attack; + } + } + if (data.name == "selfheal") { + var attack = commence_attack(player, player, data.name); + if (!attack.failed) { + resolve = attack; + } else { + reject = attack; + cool = false; + } + } + if (data.name == "darkblessing" || data.name == "warcry") { + consume_mp(player, G.skills[data.name].mp); + for (var id in instances[player.in].players) { + var target = instances[player.in].players[id]; + if ( + !target.npc && + distance(player, target, true) < G.skills[data.name].range && + (!(G.maps[player.map].pvp || is_pvp) || is_same(player, target, 1)) + ) { + target.s[G.skills[data.name].condition] = { ms: G.skills[data.name].duration, f: player.name }; + resend(target, "u+cid"); + add_pdps(player, target, 500); + } + } + xy_emit(player, "ui", { type: data.name }); + } + if (data.name == "3shot" || data.name == "5shot") { + player.halt = true; + var times = 0; + var hit = {}; + var reftarget = null; + var targets = 3; + var attack = null; + var c_resolve = null; + if (data.name == "5shot") { + targets = 5; + } + //console.log(data.ids); + if (is_array(data.ids)) { + data.ids.forEach(function (id) { + if (times >= targets) { + return; + } + times += 1; + var target = instances[player.in].monsters[id]; + if (!target) { + target = instances[player.in].players[id]; + } + if (!target || is_invinc(target) || target.name == player.name) { + attack = { failed: true, place: data.name, reason: "no_target" }; + return; + } + if (hit[id]) { + return; + } + hit[id] = true; + attack = commence_attack(player, target, data.name); + if (!attack || !attack.projectile) { + return; + } + if (!c_resolve) { + reftarget = target; + c_resolve = attack; + attack.pids = [attack.pid]; + attack.targets = [attack.target]; + } else { + c_resolve.pids.push(attack.pid); + c_resolve.targets.push(attack.target); + } + // if(times==1 && attack==null) times=40; + }); + } + consume_mp(player, G.skills[data.name].mp, reftarget); + player.halt = false; + player.to_resend = "u+cid"; + if (!c_resolve) { + if (attack) { + reject = attack; + } + disappearing_text(player.socket, player, "NO HITS"); + } else { + resolve = c_resolve; + } + } + if (data.name == "track") { + var list = []; + for (var id in instances[player.in].players) { + if (id == player.name) { + continue; + } + var target = instances[player.in].players[id]; + var current = { sound: "wmp" }; + if (target.npc) { + continue; + } + if (is_invis(target)) { + current.invis = true; + } + if (target.type == "rogue" || target.type == "ranger") { + current.sound = "rr"; + } else if (target.type == "priest" || target.type == "mage") { + current.sound = "pm"; + } + current.dist = distance(player, target); + if (current.dist < G.skills.track.range) { + list.push(current); + } + } + list.sort(function (a, b) { + return a.dist - b.dist; + }); + consume_mp(player, G.skills[data.name].mp); + xy_emit(player, "ui", { type: "track", name: player.name }); + socket.emit("track", list); + player.to_resend = "u+cid"; + } + if (data.name == "agitate") { + var ids = []; + for (var id in instances[player.in].monsters) { + var target = instances[player.in].monsters[id]; + if (target.target == player.name) { + ids.push(id); + continue; + } // why did the missing continue cause a disengage I have no idea [25/03/23] + if (target.target && !(get_player(target.target) && is_same(player, get_player(target.target), 1))) { + continue; + } + var dist = distance(player, target); + if (dist < G.skills.agitate.range) { + if (target.target) { + stop_pursuit(target, { redirect: true, cause: "agitate redirect" }); + } + target.cid += 1; + target.u = true; + target.last.attacked = new Date(); + target_player(target, player); ids.push(id); - add_pdps(player,target,500); } } + consume_mp(player, G.skills[data.name].mp, target); + xy_emit(player, "ui", { type: "agitate", name: player.name, ids: ids }); + player.to_resend = "u+cid"; + add_pdps(player, null, 1000); } - if(is_in_pvp(player,1)) - for(var id in instances[player.in].players) - { - var target=instances[player.in].players[id]; - var dist=distance(player,target); - if(dist G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + var ids = []; + if (is_same(player, target, 1)) { + consume_mp(player, G.skills[data.name].mp); + } else { + if (player.mp < G.skills[data.name].mp * 6 || player.level < 75) { + return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp * 6); + if (Math.random() < 0.95) { + consume_skill(player, data.name); + resend(player, "u+cid"); + return socket.emit("game_response", { response: "non_friendly_target", place: data.name, failed: true }); + } + } + for (var id in instances[player.in].monsters) { + var monster = instances[player.in].monsters[id]; + if (monster.target == target.name) { + stop_pursuit(monster, { redirect: true, cause: "absorb redirect" }); + monster.cid += 1; + monster.u = true; + target_player(monster, player); + ids.push(id); + } + } + xy_emit(player, "ui", { type: "absorb", name: player.name, from: data.id, ids: ids }); + player.to_resend = "u+cid"; + add_pdps(player, target, 1000); + } + if (data.name == "stomp") { + var ids = []; + var reftarget = null; + for (var id in instances[player.in].monsters) { + var target = instances[player.in].monsters[id]; + var dist = distance(player, target); + if (dist < G.skills.stomp.range) { + if (target.immune) { + player.hitchhikers.push(["game_response", { response: "skill_immune", skill: data.name }]); + player.to_resend = "nc"; + disappearing_text(target.socket, target, "IMMUNE!", { xy: 1, color: "evade", nv: 1, from: player.id }); + } else if (add_condition(target, "stunned", { duration: G.skills.stomp.duration })) { ids.push(id); - add_pdps(player,target,5000); + add_pdps(player, target, 500); } } } - consume_mp(player,G.skills[data.name].mp,reftarget); - xy_emit(player,"ui",{type:"stomp",name:player.name,ids:ids}); - player.to_resend="u+cid"; - resolve={response:"data",place:data.name,success:true,ids:ids} - } - if(data.name=="scare") - { - var ids=[]; - consume_mp(player,G.skills[data.name].mp); - for(var id in instances[player.in].monsters) - { - var target=instances[player.in].monsters[id]; - if(target.target==player.name) - { - if(target.immune) - { - player.hitchhikers.push(["game_response",{response:"skill_immune",skill:"scare"}]); - player.to_resend="nc"; - disappearing_text(target.socket,target,"IMMUNE!",{xy:1,color:"evade",nv:1,from:player.id}); - } - else - { - stop_pursuit(target,{force:true,cause:"scare"}); - target.abs=true; - target.moving=false; - ids.push(id); - } - } - } - xy_emit(player,"ui",{type:"scare",name:player.name,ids:ids}); - player.to_resend="u+cid"; - } - if(data.name=="huntersmark") - { - if(target.is_player && !is_in_pvp(player) && !is_same(player,target,1)) - return socket.emit("game_response",{response:"skill_cant_pve",place:data.name,failed:true}); - consume_mp(player,G.skills[data.name].mp,target); - add_condition(target,"marked"); - if(target.is_player) - resend(target,"u+cid"); - else - target.cid++,target.u=true; - xy_emit(player,"ui",{type:"huntersmark",name:player.name,id:target.id}); - player.to_resend="u+cid"; - } - if(data.name=="charm") - { - consume_mp(player,G.skills[data.name].mp); - if(Math.random()>0.01) - { - socket.emit("game_response","charm_failed"); - xy_emit(player,"ui",{type:"charm",name:player.name,id:target.id,fail:true}); - } - else - { - target.cid++; - target.u=true; - target.s.charmed={"ms":G.conditions.charmed.duration}; - xy_emit(player,"ui",{type:"charm",name:player.name,id:target.id}); - } - player.to_resend="u+cid"; - } - if(data.name=="cleave" || data.name=="shadowstrike") - { - player.to_resend="u+cid"; - if(data.name=="shadowstrike") - { - if(!player.items[data.num] || player.items[data.num].name!="shadowstone") - return socket.emit("game_response",{response:"skill_cant_item",place:data.name,failed:true}); - consume_one(player,data.num); - player.to_resend="u+cid+reopen"; - } - player.halt=true; - var ids=[],reftarget=null,c_resolve=null; - for(var id in instances[player.in].monsters) - { - var target=instances[player.in].monsters[id]; - var dist=distance(player,target); - if(dist 0.01) { + socket.emit("game_response", "charm_failed"); + xy_emit(player, "ui", { type: "charm", name: player.name, id: target.id, fail: true }); + } else { + target.cid++; + target.u = true; + target.s.charmed = { ms: G.conditions.charmed.duration }; + xy_emit(player, "ui", { type: "charm", name: player.name, id: target.id }); + } + player.to_resend = "u+cid"; + } + if (data.name == "cleave" || data.name == "shadowstrike") { + player.to_resend = "u+cid"; + if (data.name == "shadowstrike") { + if (!player.items[data.num] || player.items[data.num].name != "shadowstone") { + return socket.emit("game_response", { response: "skill_cant_item", place: data.name, failed: true }); + } + consume_one(player, data.num); + player.to_resend = "u+cid+reopen"; + } + player.halt = true; + var ids = []; + var reftarget = null; + var c_resolve = null; + for (var id in instances[player.in].monsters) { + var target = instances[player.in].monsters[id]; + var dist = distance(player, target); + if (dist < G.skills[data.name].range) { + attack = commence_attack(player, target, data.name); + if (!attack || !attack.projectile) { + continue; + } + ids.push(id); + if (!c_resolve) { + c_resolve = attack; + attack.pids = [attack.pid]; + attack.targets = [attack.target]; + } else { c_resolve.pids.push(attack.pid); c_resolve.targets.push(attack.target); } } } - consume_mp(player,G.skills[data.name].mp,reftarget); - xy_emit(player,"ui",{type:data.name,name:player.name,ids:ids}); - player.halt=false; - if(c_resolve) resolve=c_resolve; - } - if(data.name=="magiport") - { - var pported=false; - consume_mp(player,G.skills[data.name].mp); - if(!is_pvp && mode.pve_safe_magiports) - { - if(!magiportations[player.name]) magiportations[player.name]={}; - magiportations[player.name][target.name]=true; - target.socket.emit("magiport",{name:player.name}); - player.socket.emit("game_response",{response:"magiport_sent",id:data.id}); - xy_emit(player,"ui",{type:"magiport",name:player.name}); - - } - else - { - if((is_same(player,target,1) || !target.slots.helmet)) - { - ported=magiport_someone(target,player); - } - if(!ported) player.socket.emit("game_response",{response:"magiport_failed",id:data.id}); - } - player.to_resend="u+cid"; - } - if(data.name=="blink") - { - var x=parseFloat(data.x)||0,y=parseFloat(data.y)||0; - var spot=safe_xy_nearby(player.map,x,y); - if(!spot) return player.socket.emit("game_response",{response:"blink_failed",place:data.name,failed:true}); - player.s.blink={ms:200}; - player.s.blink.in=player.in; - player.s.blink.map=player.map; - player.s.blink.x=spot.x; - player.s.blink.y=spot.y; - player.s.blink.d=0; if(in_arr(data.direction,[1,2,3])) player.s.blink.d=data.direction; - if(player.role!="gm") consume_mp(player,G.skills[data.name].mp); - // xy_emit(player,"ui",{type:"blinking",name:player.name}); - resend(player,"u+cid"); - // #TODO: Appear animation for non-self's [21/05/18] - } - if(data.name=="warp") - { - var x=parseFloat(data.x)||0,y=parseFloat(data.y)||0,ins=data.in||"main",instance=instances[ins]; - if(!instance) return player.socket.emit("game_response",{response:"blink_failed",place:data.name,failed:true}); - if(instance.mount!=instances[player.in].mount) return player.socket.emit("game_response",{response:"cant_in_bank",place:data.name,failed:true}); - var spot=safe_xy_nearby(instance.name,x,y); - if(!spot) return player.socket.emit("game_response",{response:"blink_failed",place:data.name,failed:true}); - player.s.blink={ms:200}; - player.s.blink.in=ins; - player.s.blink.map=instance.name; - player.s.blink.x=spot.x; - player.s.blink.y=spot.y; - player.s.blink.d=0; if(in_arr(data.direction,[1,2,3])) player.s.blink.d=data.direction; - if(player.role!="gm") consume_mp(player,G.skills[data.name].mp); - // xy_emit(player,"ui",{type:"blinking",name:player.name}); - player.to_resend="u+cid"; - // #TODO: Appear animation for non-self's [21/05/18] - } - if(data.name=="mluck") - { - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - consume_mp(player,G.skills[data.name].mp); - if(!target.s[G.skills[data.name].condition] || !target.s[G.skills[data.name].condition].strong || target.s[G.skills[data.name].condition].f==player.name) target.s[G.skills[data.name].condition]={ms:G.conditions.mluck.duration,f:player.name}; - if(target.owner==player.owner) target.s[G.skills[data.name].condition].strong=true; - xy_emit(player,"ui",{type:"mluck",from:player.name,to:target.name}); - resend(target,"u+cid"); resend(player,"u+cid"); - } - if(data.name=="rspeed") - { - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - consume_mp(player,G.skills[data.name].mp); - target.s[G.skills[data.name].condition]={ms:G.conditions.rspeed.duration,f:player.name}; - xy_emit(player,"ui",{type:"rspeed",from:player.name,to:target.name}); - resend(target,"u+cid"); resend(player,"u+cid"); - if(player.party==target.party && player!=target) add_pdps(player,target,2000); - } - if(data.name=="reflection") - { - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - consume_mp(player,G.skills[data.name].mp); - target.s[G.skills[data.name].condition]={ms:G.conditions.reflection.duration,f:player.name}; - xy_emit(player,"ui",{type:"reflection",from:player.name,to:target.name}); - resend(target,"u+cid"); resend(player,"u+cid"); - if(player.party==target.party && player!=target) add_pdps(player,target,4000); - } - if(data.name=="energize") - { - if(distance(player,target,true)>G.skills[data.name].range) - return socket.emit("game_response",{response:"too_far",place:data.name,failed:true}); - if(!player.mp) - return socket.emit("game_response",{response:"no_mp",place:data.name,failed:true}); - var mp=parseInt(data.mp)||10000; - mp=max(1,min(player.mp,mp)); - if(mp>target.max_mp-target.mp) mp=target.max_mp-target.mp; - target.mp+=mp; player.mp-=mp; - if(player.party==target.party && player!=target) add_pdps(player,target,mp*2); - disappearing_text(player.socket,player,"-"+mp,{color:"mana",xy:1}); - disappearing_text(target.socket,target,"+"+mp,{color:"mana",xy:1}); - target.s[G.skills[data.name].condition]={ms:G.conditions[G.skills[data.name].condition].duration}; - xy_emit(player,"ui",{type:"energize",from:player.name,to:target.name}); - resend(target,"u+cid"); resend(player,"u+cid"); - } - if(data.name=="alchemy") - { - var gold=0,rate=0.8; - if(player.level>=100) rate=1.12; - else if(player.level>=90) rate=1.10; - else if(player.level>=80) rate=1.06; - else if(player.level>=70) rate=1; - else if(player.level>=60) rate=0.92; - else if(player.level>=50) rate=0.86; - consume_mp(player,G.skills[data.name].mp); - xy_emit(player,"ui",{type:"alchemy",name:player.name}); - for(var i=0;i300) return socket.emit("game_response","distance"); - if(!player.s.holidayspirit && player.esize) add_item(player,"funtoken"),x="+reopen"; - add_condition(player,"holidayspirit"); - resend(player,"u+cid"+x); - } - else if(["redorb","blueorb","greenorb","yelloworb"].includes(data.type)) - { - if(!G.maps[player.map].ref || !G.maps[player.map].ref[data.type] || distance(G.maps[player.map].ref[data.type],player,true)>40) return socket.emit("game_response","distance"); - ["redorb","blueorb","greenorb","yelloworb"].forEach(function(s){ delete player.s[s]; }) - add_condition(player,data.type); - resend(player,"u+cid"); - } - else if(data=="the_lever") - { - if(player.map!="resort_e") return socket.emit("game_response","distance"); - player.s.magiport={ms:300}; - player.s.magiport.x=player.x; - player.s.magiport.y=player.y; - player.s.magiport.f=player.name; - player.s.magiport.in="resort"; - player.s.magiport.map="resort"; - resend(player,"u+cid"); - } - else if(data.type=="dailytask") - { - if(!G.maps.main.ref.dailytask || distance(G.maps.main.ref.dailytask,player,true)>150) return socket.emit("game_response","distance"); - player.p.monsterhunt={ms:60*60*1000,m:"goo"}; - socket.emit("game_response",{"response":"monsterhunt",monster:"goo"}); - } - else if(data.key && player.konami) - { - if(data.key=="B" && player.konami.length<20) player.konami.push(data.key[0]); - else if(data.key=="A") - { - if(player.konami.join("")=="uuddlrlrB") - { - player.tskin="konami",resend(player,"u+cid"); - if(!player.p.target_lock || !G.monsters[player.p.target_lock] || hsince(player.p.dt.last_tl)>15*24) - { - var monsters=[]; - for(var name in G.monsters) if(!G.monsters[name].special && !G.monsters[name].stationary && G.monsters[name].c) monsters.push(name); - player.p.target_lock=random_one(monsters); - player.p.dt.last_tl=new Date(); - } - socket.emit("game_response",{response:"target_lock",monster:player.p.target_lock}); - } - else player.konami=[]; - } - } - }); - socket.on('mreport',function(data){ - var player=players[socket.id]; if(!player) return; - var a=0,b=0; - a=parseFloat(data.x)||0; - b=parseFloat(data.y)||0; - // console.log(JSON.stringify(data)); - socket.emit("game_log",""+simple_distance({x:player.x,y:player.y},{x:a,y:b})); - }) - socket.on('move',function(data){ - // if(observers[socket.id]) observers[socket.id].x=data.x,observers[socket.id].y=data.y; Old observer code [17/02/17] - var player=players[socket.id],current=-1,going=-1,actual=true; - var x=parseFloat(data.going_x)||0,y=parseFloat(data.going_y)||0; - if(data.pet) player=player.monster,actual=false; - if(data.key && (!player.konami || player.konami.length<20)) - { - // console.log(data.key); - if(!player.konami) player.konami=[]; - player.konami.push(data.key[0]); - } - if(player && player.m==data.m && can_walk(player) && (x!=player.x || y!=player.y)) - { - // if(is_sdk) server_log("Player moving to: "+data.going_x+","+data.going_y); - - // player.x=data.x; player.y=data.y; [03/08/16] Seems like a really bad idea to update x/y based on what players provide - data.x=parseFloat(data.x)||0; - data.y=parseFloat(data.y)||0; - player.going_x=x; - player.going_y=y; - if(smap_data[player.map]!=-1 && mode.enforce_smap) - { - current=smap_data[player.map][rphash(data.x,data.y)],going=smap_data[player.map][rphash(player.going_x,player.going_y)]; - // server_log("current:"+current+" going:"+going+" real:"+smap_data[player.map][phash(player.x,player.y)]); - if((current===undefined || current>=2) || (going===undefined || going>=2)) - { - server_log("#C cheater: "+player.name+" current: "+current+"["+data.x+","+data.x+"] going: "+going+"["+player.going_x+","+player.going_y+"] at "+player.map,1); - appengine_log("violation","move_line: "+player.name+" afk: "+player.afk+" code: "+player.code); - player.socket.emit("game_log","Line violation detected"); - player.socket.emit("game_log","Make sure you only move with the built-in move function"); - defeat_player(player); - transport_player_to(player,"jail"); - return; - } - } - // player.check_x=player.going_x; player.check_y=player.going_y; player.checked_xy=false; - start_moving_element(player); - if(!player.pet && simple_distance(player,{x:data.x,y:data.y})>132 && current==0 && mode.lcorrection) - { - // server_log("Correction sent"); - socket.emit("correction",{x:player.x,y:player.y}); - } - if(actual) - { - var change=false; - for(var id in player.c) - { - if(G.conditions[id] && !G.conditions[id].can_move) - { - change=true; - delete player.c[id]; + if (is_in_pvp(player, 1)) { + for (var id in instances[player.in].players) { + var target = instances[player.in].players[id]; + var dist = distance(player, target); + if (dist < G.skills[data.name].range && !target.npc && !is_same(player, target, 1)) { + attack = commence_attack(player, target, data.name); + if (!attack || !attack.projectile) { + continue; + } + ids.push(id); + if (!c_resolve) { + reftarget = target; + c_resolve = attack; + attack.pids = [attack.pid]; + attack.targets = [attack.target]; + } else { + c_resolve.pids.push(attack.pid); + c_resolve.targets.push(attack.target); + } + } } } - if(change) resend(player,"u+cid"); + consume_mp(player, G.skills[data.name].mp, reftarget); + xy_emit(player, "ui", { type: data.name, name: player.name, ids: ids }); + player.halt = false; + if (c_resolve) { + resolve = c_resolve; + } } - //if(smap_data[player.map][rphash(player.x,player.y)]===0) push_xyh(player,player.x,player.y); - //else push_xyh(player,data.x,data.y); - } - }); - socket.on('open_chest',function(data){ - var chest=chests[data.id],player=players[socket.id],reopen=false; if(!player) return; - var r={id:data.id,goldm:player.goldm,opener:player.name,items:[]}; - if(chest && simple_distance(chest,player)>400) r.goldm=1,r.dry=true; - if(chest && msince(chest.date)>8) r.goldm=1,r.stale=true; - if(player.map=="woffice" || G.maps[player.map].mount || is_invis(player)) return fail_response("loot_failed"); - try{ - if(chest && !chest.pvp && chest.gold>100000000 && !player.stealth) - { - broadcast("server_message",{"message":player.name+" looted "+to_pretty_num(server_tax(round(chest.gold*r.goldm),true))+" gold","color":"gold","type":"server_gold",name:player.name}); + if (data.name == "magiport") { + var pported = false; + consume_mp(player, G.skills[data.name].mp); + if (!is_pvp && mode.pve_safe_magiports) { + if (!magiportations[player.name]) { + magiportations[player.name] = {}; + } + magiportations[player.name][target.name] = true; + target.socket.emit("magiport", { name: player.name }); + player.socket.emit("game_response", { response: "magiport_sent", id: data.id }); + xy_emit(player, "ui", { type: "magiport", name: player.name }); + } else { + if (is_same(player, target, 1) || !target.slots.helmet) { + ported = magiport_someone(target, player); + } + if (!ported) { + player.socket.emit("game_response", { response: "magiport_failed", id: data.id }); + } + } + player.to_resend = "u+cid"; } - if(chest && player.owner && chest.owners && !in_arr(player.owner,chest.owners) && !W.chest[player.owner]) - { - W.chest[player.owner]=new Date(); - server_log("SEVERE - Cross Loot from "+player.name+" not from "+chest.owners.toString()); + if (data.name == "blink") { + var x = parseFloat(data.x) || 0; + var y = parseFloat(data.y) || 0; + var spot = safe_xy_nearby(player.map, x, y); + if (!spot) { + return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); + } + player.s.blink = { ms: 200 }; + player.s.blink.in = player.in; + player.s.blink.map = player.map; + player.s.blink.x = spot.x; + player.s.blink.y = spot.y; + player.s.blink.d = 0; + if (in_arr(data.direction, [1, 2, 3])) { + player.s.blink.d = data.direction; + } + if (player.role != "gm") { + consume_mp(player, G.skills[data.name].mp); + } + // xy_emit(player,"ui",{type:"blinking",name:player.name}); + resend(player, "u+cid"); + // #TODO: Appear animation for non-self's [21/05/18] } - if(chest && !player.party) - { - var all_items=chest.items.slice(0); all_items.concat(chest.pvp_items) - if(!can_add_items(player,all_items)) return fail_response("loot_no_space"); - delete chests[data.id]; // The add_shells routine was at the top, so when inventory was full, attempting to open the chest gave shells infinitely, repeated lesson, always remove before adding, or exceptions [11/07/18] - if(chest && chest.cash) add_shells(player,chest.cash,"chest",true,"override"); - chest.items.forEach(function(item){ - item.src="pvp"; - add_item(player,item,{found:1,m:1,v:B.v}); reopen=true; - var ritem=cache_item(item); ritem.looter=player.name; r.items.push(ritem); - socket.emit("game_log",{message:"Found "+item_to_phrase(item),color:"#4BAEAA"}); - if(player.t) player.t.dgold+=round(G.items[item.name].g*0.6); + if (data.name == "warp") { + var x = parseFloat(data.x) || 0; + var y = parseFloat(data.y) || 0; + var ins = data.in || "main"; + var instance = instances[ins]; + if (!instance) { + return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); + } + if (instance.mount != instances[player.in].mount) { + return player.socket.emit("game_response", { response: "cant_in_bank", place: data.name, failed: true }); + } + var spot = safe_xy_nearby(instance.name, x, y); + if (!spot) { + return player.socket.emit("game_response", { response: "blink_failed", place: data.name, failed: true }); + } + player.s.blink = { ms: 200 }; + player.s.blink.in = ins; + player.s.blink.map = instance.name; + player.s.blink.x = spot.x; + player.s.blink.y = spot.y; + player.s.blink.d = 0; + if (in_arr(data.direction, [1, 2, 3])) { + player.s.blink.d = data.direction; + } + if (player.role != "gm") { + consume_mp(player, G.skills[data.name].mp); + } + // xy_emit(player,"ui",{type:"blinking",name:player.name}); + player.to_resend = "u+cid"; + // #TODO: Appear animation for non-self's [21/05/18] + } + if (data.name == "mluck") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp); + if ( + !target.s[G.skills[data.name].condition] || + !target.s[G.skills[data.name].condition].strong || + target.s[G.skills[data.name].condition].f == player.name + ) { + target.s[G.skills[data.name].condition] = { ms: G.conditions.mluck.duration, f: player.name }; + } + if (target.owner == player.owner) { + target.s[G.skills[data.name].condition].strong = true; + } + xy_emit(player, "ui", { type: "mluck", from: player.name, to: target.name }); + resend(target, "u+cid"); + resend(player, "u+cid"); + } + if (data.name == "rspeed") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp); + target.s[G.skills[data.name].condition] = { ms: G.conditions.rspeed.duration, f: player.name }; + xy_emit(player, "ui", { type: "rspeed", from: player.name, to: target.name }); + resend(target, "u+cid"); + resend(player, "u+cid"); + if (player.party == target.party && player != target) { + add_pdps(player, target, 2000); + } + } + if (data.name == "reflection") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + consume_mp(player, G.skills[data.name].mp); + target.s[G.skills[data.name].condition] = { ms: G.conditions.reflection.duration, f: player.name }; + xy_emit(player, "ui", { type: "reflection", from: player.name, to: target.name }); + resend(target, "u+cid"); + resend(player, "u+cid"); + if (player.party == target.party && player != target) { + add_pdps(player, target, 4000); + } + } + if (data.name == "energize") { + if (distance(player, target, true) > G.skills[data.name].range) { + return socket.emit("game_response", { response: "too_far", place: data.name, failed: true }); + } + if (!player.mp) { + return socket.emit("game_response", { response: "no_mp", place: data.name, failed: true }); + } + var mp = parseInt(data.mp) || 10000; + mp = max(1, min(player.mp, mp)); + if (mp > target.max_mp - target.mp) { + mp = target.max_mp - target.mp; + } + target.mp += mp; + player.mp -= mp; + if (player.party == target.party && player != target) { + add_pdps(player, target, mp * 2); + } + disappearing_text(player.socket, player, "-" + mp, { color: "mana", xy: 1 }); + disappearing_text(target.socket, target, "+" + mp, { color: "mana", xy: 1 }); + target.s[G.skills[data.name].condition] = { ms: G.conditions[G.skills[data.name].condition].duration }; + xy_emit(player, "ui", { type: "energize", from: player.name, to: target.name }); + resend(target, "u+cid"); + resend(player, "u+cid"); + } + if (data.name == "alchemy") { + var gold = 0; + var rate = 0.8; + if (player.level >= 100) { + rate = 1.12; + } else if (player.level >= 90) { + rate = 1.1; + } else if (player.level >= 80) { + rate = 1.06; + } else if (player.level >= 70) { + rate = 1; + } else if (player.level >= 60) { + rate = 0.92; + } else if (player.level >= 50) { + rate = 0.86; + } + consume_mp(player, G.skills[data.name].mp); + xy_emit(player, "ui", { type: "alchemy", name: player.name }); + for (var i = 0; i < player.isize; i++) { + if (!player.items[i] || player.items[i].l) { + continue; + } + gold = calculate_item_value(player.items[i]); + consume_one(player, i); + break; + } + player.gold += gold * rate; + resend(player, "reopen"); + socket.emit("game_response", { response: "gold_received", gold: gold * rate }); + } + if (cool) { + consume_skill(player, data.name); + } + if (player.to_resend) { + resend(player, player.to_resend); + } + if (reject) { + if (!reject.response) { + reject.response = "data"; + } + socket.emit("game_response", reject); + } else if (resolve) { + socket.emit("game_response", resolve); + } + }); + socket.on("click", function (data) { + // You'll be missed 'click' method, the 'click' method was the first method on this server, it was used as an attack method up until [17/06/18] - at this date, there were 3 simple conditions left which checked for data.button=="right" - the game matured so that all interactions were handled client-side rather than processed server-side + socket.emit("game_log", "'click' method is deprecated."); + }); + socket.on("attack", function (data) { + return socket.fs.skill({ name: "attack", id: data.id }); + }); + socket.on("heal", function (data) { + return socket.fs.skill({ name: "heal", id: data.id }); + }); + socket.on("interaction", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.type == "newyear_tree") { + var x = ""; + if ( + !G.maps[player.map].ref || + !G.maps[player.map].ref[data.type] || + distance(G.maps[player.map].ref[data.type], player, true) > 300 + ) { + return socket.emit("game_response", "distance"); + } + if (!player.s.holidayspirit && player.esize) { + add_item(player, "funtoken"); + x = "+reopen"; + } + add_condition(player, "holidayspirit"); + resend(player, "u+cid" + x); + } else if (["redorb", "blueorb", "greenorb", "yelloworb"].includes(data.type)) { + if ( + !G.maps[player.map].ref || + !G.maps[player.map].ref[data.type] || + distance(G.maps[player.map].ref[data.type], player, true) > 40 + ) { + return socket.emit("game_response", "distance"); + } + ["redorb", "blueorb", "greenorb", "yelloworb"].forEach(function (s) { + delete player.s[s]; }); - (chest.pvp_items||[]).forEach(function(item){ - item.v=new Date(); - if(can_add_item(player,item)) - { - add_item(player,item,{found:1,v:B.v}); reopen=true; - var ritem=cache_item(item); ritem.looter=player.name; ritem.pvp_loot=true; r.items.push(ritem); - socket.emit("game_log",{message:"Looted "+item_to_phrase(item),color:"#4BAEAA"}); + add_condition(player, data.type); + resend(player, "u+cid"); + } else if (data == "the_lever") { + if (player.map != "resort_e") { + return socket.emit("game_response", "distance"); + } + player.s.magiport = { ms: 300 }; + player.s.magiport.x = player.x; + player.s.magiport.y = player.y; + player.s.magiport.f = player.name; + player.s.magiport.in = "resort"; + player.s.magiport.map = "resort"; + resend(player, "u+cid"); + } else if (data.type == "dailytask") { + if (!G.maps.main.ref.dailytask || distance(G.maps.main.ref.dailytask, player, true) > 150) { + return socket.emit("game_response", "distance"); + } + player.p.monsterhunt = { ms: 60 * 60 * 1000, m: "goo" }; + socket.emit("game_response", { response: "monsterhunt", monster: "goo" }); + } else if (data.key && player.konami) { + if (data.key == "B" && player.konami.length < 20) { + player.konami.push(data.key[0]); + } else if (data.key == "A") { + if (player.konami.join("") == "uuddlrlrB") { + player.tskin = "konami"; + resend(player, "u+cid"); + if (!player.p.target_lock || !G.monsters[player.p.target_lock] || hsince(player.p.dt.last_tl) > 15 * 24) { + var monsters = []; + for (var name in G.monsters) { + if (!G.monsters[name].special && !G.monsters[name].stationary && G.monsters[name].c) { + monsters.push(name); + } + } + player.p.target_lock = random_one(monsters); + player.p.dt.last_tl = new Date(); + } + socket.emit("game_response", { response: "target_lock", monster: player.p.target_lock }); + } else { + player.konami = []; } - else - { - lostandfound_logic(item); - var ritem=cache_item(item); ritem.looter=null; ritem.lostandfound=true; r.items.push(ritem); - socket.emit("game_log",{message:"Lost "+item_to_phrase(item),color:"#AB4E4F"}); - } - }); - r.gold=round(chest.gold*r.goldm)+round(chest.egold||0); - r.gold=server_tax(r.gold); - player.gold+=r.gold; - if(player.t) player.t.cgold+=r.gold; - if(r.gold) socket.emit("game_log",{message:to_pretty_num(r.gold)+" gold",color:"gold"}); - socket.emit("disappearing_text",{"message":"+"+r.gold,"x":chest.x,"y":chest.y-10,"args":{color:"+gold","size":"large"}}); - if(!r.items.length) delete r.items; - resend(player,reopen&&"reopen+nc+inv"||""); - socket.emit("chest_opened",r); + } } - else if(chest) - { - // var gold=round(chest.gold/parties[player.party].length); - r.party=true; - var chest=chests[data.id],reopen={}; - delete chests[data.id]; - if(chest && chest.cash) add_shells(player,chest.cash,"chest",true,"override"); - chest.items.forEach(function(item){ + }); + socket.on("mreport", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var a = 0; + var b = 0; + a = parseFloat(data.x) || 0; + b = parseFloat(data.y) || 0; + // console.log(JSON.stringify(data)); + socket.emit("game_log", "" + simple_distance({ x: player.x, y: player.y }, { x: a, y: b })); + }); + socket.on("move", function (data) { + // if(observers[socket.id]) observers[socket.id].x=data.x,observers[socket.id].y=data.y; Old observer code [17/02/17] + var player = players[socket.id]; + var current = -1; + var going = -1; + var actual = true; + var x = parseFloat(data.going_x) || 0; + var y = parseFloat(data.going_y) || 0; + if (data.pet) { + player = player.monster; + actual = false; + } + if (data.key && (!player.konami || player.konami.length < 20)) { + // console.log(data.key); + if (!player.konami) { + player.konami = []; + } + player.konami.push(data.key[0]); + } + if (player && player.m == data.m && can_walk(player) && (x != player.x || y != player.y)) { + // if(is_sdk) server_log("Player moving to: "+data.going_x+","+data.going_y); + + // player.x=data.x; player.y=data.y; [03/08/16] Seems like a really bad idea to update x/y based on what players provide + data.x = parseFloat(data.x) || 0; + data.y = parseFloat(data.y) || 0; + player.going_x = x; + player.going_y = y; + if (smap_data[player.map] != -1 && mode.enforce_smap) { + current = smap_data[player.map][rphash(data.x, data.y)]; + going = smap_data[player.map][rphash(player.going_x, player.going_y)]; + // server_log("current:"+current+" going:"+going+" real:"+smap_data[player.map][phash(player.x,player.y)]); + if (current === undefined || current >= 2 || going === undefined || going >= 2) { + server_log( + "#C cheater: " + + player.name + + " current: " + + current + + "[" + + data.x + + "," + + data.x + + "] going: " + + going + + "[" + + player.going_x + + "," + + player.going_y + + "] at " + + player.map, + 1, + ); + appengine_log("violation", "move_line: " + player.name + " afk: " + player.afk + " code: " + player.code); + player.socket.emit("game_log", "Line violation detected"); + player.socket.emit("game_log", "Make sure you only move with the built-in move function"); + defeat_player(player); + transport_player_to(player, "jail"); + return; + } + } + // player.check_x=player.going_x; player.check_y=player.going_y; player.checked_xy=false; + start_moving_element(player); + if ( + !player.pet && + simple_distance(player, { x: data.x, y: data.y }) > 132 && + current == 0 && + mode.lcorrection + ) { + // server_log("Correction sent"); + socket.emit("correction", { x: player.x, y: player.y }); + } + if (actual) { + var change = false; + for (var id in player.c) { + if (G.conditions[id] && !G.conditions[id].can_move) { + change = true; + delete player.c[id]; + } + } + if (change) { + resend(player, "u+cid"); + } + } + //if(smap_data[player.map][rphash(player.x,player.y)]===0) push_xyh(player,player.x,player.y); + //else push_xyh(player,data.x,data.y); + } + }); + socket.on("open_chest", function (data) { + var chest = chests[data.id]; + var player = players[socket.id]; + var reopen = false; + if (!player) { + return; + } + var r = { id: data.id, goldm: player.goldm, opener: player.name, items: [] }; + if (chest && simple_distance(chest, player) > 400) { + r.goldm = 1; + r.dry = true; + } + if (chest && msince(chest.date) > 8) { + r.goldm = 1; + r.stale = true; + } + if (player.map == "woffice" || G.maps[player.map].mount || is_invis(player)) { + return fail_response("loot_failed"); + } + try { + if (chest && !chest.pvp && chest.gold > 100000000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " looted " + to_pretty_num(server_tax(round(chest.gold * r.goldm), true)) + " gold", + color: "gold", + type: "server_gold", + name: player.name, + }); + } + if (chest && player.owner && chest.owners && !in_arr(player.owner, chest.owners) && !W.chest[player.owner]) { + W.chest[player.owner] = new Date(); + server_log("SEVERE - Cross Loot from " + player.name + " not from " + chest.owners.toString()); + } + if (chest && !player.party) { + var all_items = chest.items.slice(0); + all_items.concat(chest.pvp_items); + if (!can_add_items(player, all_items)) { + return fail_response("loot_no_space"); + } + delete chests[data.id]; // The add_shells routine was at the top, so when inventory was full, attempting to open the chest gave shells infinitely, repeated lesson, always remove before adding, or exceptions [11/07/18] + if (chest && chest.cash) { + add_shells(player, chest.cash, "chest", true, "override"); + } + chest.items.forEach(function (item) { + item.src = "pvp"; + add_item(player, item, { found: 1, m: 1, v: B.v }); + reopen = true; + var ritem = cache_item(item); + ritem.looter = player.name; + r.items.push(ritem); + socket.emit("game_log", { message: "Found " + item_to_phrase(item), color: "#4BAEAA" }); + if (player.t) { + player.t.dgold += round(G.items[item.name].g * 0.6); + } + }); + (chest.pvp_items || []).forEach(function (item) { + item.v = new Date(); + if (can_add_item(player, item)) { + add_item(player, item, { found: 1, v: B.v }); + reopen = true; + var ritem = cache_item(item); + ritem.looter = player.name; + ritem.pvp_loot = true; + r.items.push(ritem); + socket.emit("game_log", { message: "Looted " + item_to_phrase(item), color: "#4BAEAA" }); + } else { + lostandfound_logic(item); + var ritem = cache_item(item); + ritem.looter = null; + ritem.lostandfound = true; + r.items.push(ritem); + socket.emit("game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); + } + }); + r.gold = round(chest.gold * r.goldm) + round(chest.egold || 0); + r.gold = server_tax(r.gold); + player.gold += r.gold; + if (player.t) { + player.t.cgold += r.gold; + } + if (r.gold) { + socket.emit("game_log", { message: to_pretty_num(r.gold) + " gold", color: "gold" }); + } + socket.emit("disappearing_text", { + message: "+" + r.gold, + x: chest.x, + y: chest.y - 10, + args: { color: "+gold", size: "large" }, + }); + if (!r.items.length) { + delete r.items; + } + resend(player, (reopen && "reopen+nc+inv") || ""); + socket.emit("chest_opened", r); + } else if (chest) { + // var gold=round(chest.gold/parties[player.party].length); + r.party = true; + var chest = chests[data.id]; + var reopen = {}; + delete chests[data.id]; + if (chest && chest.cash) { + add_shells(player, chest.cash, "chest", true, "override"); + } + chest.items.forEach(function (item) { // console.log(item); - var pool=0,can={}; - parties[player.party].forEach(function(name){ - var current=players[name_to_id[name]]; - if(current && can_add_item(current,item)) pool+=current.share,can[name]=true; - }); - var pool_winner=Math.random()*pool,pool_current=0,awarded=false; - parties[player.party].forEach(function(name){ - if(awarded) return; - var current=players[name_to_id[name]]; - if(current && can[name]) - { - if(pool_winner<=pool_current+current.share) - { - awarded=true; - add_item(current,item,{found:1,m:1,v:B.v}); reopen[current.id]=true; - var ritem=cache_item(item); ritem.looter=current.name; r.items.push(ritem); - party_emit(player.party,"game_log",{message:current.name+" found "+item_to_phrase(item),color:"#4BAEAA"}); - if(current.t) current.t.dgold+=round(G.items[item.name].g*0.6); + var pool = 0; + var can = {}; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && can_add_item(current, item)) { + pool += current.share; + can[name] = true; } - else pool_current+=current.share; + }); + var pool_winner = Math.random() * pool; + var pool_current = 0; + var awarded = false; + parties[player.party].forEach(function (name) { + if (awarded) { + return; + } + var current = players[name_to_id[name]]; + if (current && can[name]) { + if (pool_winner <= pool_current + current.share) { + awarded = true; + add_item(current, item, { found: 1, m: 1, v: B.v }); + reopen[current.id] = true; + var ritem = cache_item(item); + ritem.looter = current.name; + r.items.push(ritem); + party_emit(player.party, "game_log", { + message: current.name + " found " + item_to_phrase(item), + color: "#4BAEAA", + }); + if (current.t) { + current.t.dgold += round(G.items[item.name].g * 0.6); + } + } else { + pool_current += current.share; + } + } + }); + if (!awarded) { + lostandfound_logic(item); + var ritem = cache_item(item); + ritem.looter = null; + ritem.lostandfound = true; + r.items.push(ritem); + party_emit(player.party, "game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); } }); - if(!awarded) - { - lostandfound_logic(item); - var ritem=cache_item(item); ritem.looter=null; ritem.lostandfound=true; r.items.push(ritem); - party_emit(player.party,"game_log",{message:"Lost "+item_to_phrase(item),color:"#AB4E4F"}); - } - }); - (chest.pvp_items||[]).forEach(function(item){ - item.v=new Date(); - var pool=0,can={}; - parties[player.party].forEach(function(name){ - var current=players[name_to_id[name]]; - if(current && current.share && can_add_item(current,item)) pool+=current.share,can[name]=true; - }); - var pool_winner=Math.random()*pool,pool_current=0,awarded=false; - parties[player.party].forEach(function(name){ - if(awarded) return; - var current=players[name_to_id[name]]; - if(current && can[name]) - { - if(pool_winner<=pool_current+current.share) - { - awarded=true; - add_item(current,item,{found:1,v:B.v}); reopen[current.id]=true; - var ritem=cache_item(item); ritem.looter=current.name; ritem.pvp_loot=true; r.items.push(ritem); - party_emit(player.party,"game_log",{message:current.name+" looted "+item_to_phrase(item),color:"#4BAEAA"}); + (chest.pvp_items || []).forEach(function (item) { + item.v = new Date(); + var pool = 0; + var can = {}; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && current.share && can_add_item(current, item)) { + pool += current.share; + can[name] = true; } - else pool_current+=current.share; + }); + var pool_winner = Math.random() * pool; + var pool_current = 0; + var awarded = false; + parties[player.party].forEach(function (name) { + if (awarded) { + return; + } + var current = players[name_to_id[name]]; + if (current && can[name]) { + if (pool_winner <= pool_current + current.share) { + awarded = true; + add_item(current, item, { found: 1, v: B.v }); + reopen[current.id] = true; + var ritem = cache_item(item); + ritem.looter = current.name; + ritem.pvp_loot = true; + r.items.push(ritem); + party_emit(player.party, "game_log", { + message: current.name + " looted " + item_to_phrase(item), + color: "#4BAEAA", + }); + } else { + pool_current += current.share; + } + } + }); + if (!awarded) { + lostandfound_logic(item); + var ritem = cache_item(item); + ritem.looter = null; + ritem.pvp_loot = true; + ritem.lostandfound = true; + r.items.push(ritem); + party_emit(player.party, "game_log", { message: "Lost " + item_to_phrase(item), color: "#AB4E4F" }); } }); - if(!awarded) - { - lostandfound_logic(item); - var ritem=cache_item(item); ritem.looter=null; ritem.pvp_loot=true; ritem.lostandfound=true; r.items.push(ritem); - party_emit(player.party,"game_log",{message:"Lost "+item_to_phrase(item),color:"#AB4E4F"}); + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + var cgold = + round(chest.gold * (current.share || 0) * r.goldm) + round((chest.egold || 0) * (current.share || 0)); + r.gold = cgold = server_tax(cgold); + current.gold += cgold; + if (current.t) { + current.t.cgold += cgold; + } + if (cgold) { + current.socket.emit("game_log", { message: to_pretty_num(cgold) + " gold", color: "gold" }); + } + if (current.in == player.in) { + current.socket.emit("disappearing_text", { + message: "+" + cgold, + x: chest.x, + y: chest.y - 10, + args: { color: "gold", size: "large" }, + }); + } + resend(current, (reopen[current.id] && "reopen+nc+inv") || ""); + current.socket.emit("chest_opened", r); + }); + } else { + socket.emit("chest_opened", { id: data.id, gone: true }); + } + } catch (e) { + delete chests[data.id]; // If this didn't exist, any exception would end up being a source for infinite gold and items + log_trace("#X chest_error", e); + return fail_response("error"); + } + }); + socket.on("auth", function (data) { + if (gameplay == "test" && data.passphrase != "potato salad") { + return socket.emit("game_log", "Wrong passphrase!"); + } + if (observers[socket.id] && observers[socket.id].auth_engaged) { + return socket.emit("game_log", "Authorization in progress."); + } + if (dc_players[data.character]) { + return socket.emit("game_log", "Authorization in progress."); + } + if (!server.live || !observers[socket.id] || players[socket.id]) { + return; + } + if (Object.keys(players).length >= max_players) { + socket.emit("game_error", "Can't accept more than " + max_players + " players at this time"); + return; + } + socket.observer_secret = randomStr(24); + observers[socket.id].auth_engaged = true; + appengine_call( + "start_character", + { + auth: data.user + "-" + data.auth, + secret: socket.observer_secret, + code_slot: data.code_slot, + character: data.character, + mode: gameplay, + ip: get_ip(socket), + suffix: "/" + data.character, + }, + function (result) { + if (observers[socket.id]) { + observers[socket.id].auth_engaged = false; + } + if (result.failed) { + socket.emit("game_error", "Failed: " + result.reason); + return; + } + // console.log(JSON.stringify(result)); + server_log("start_character: " + JSON.stringify(result.character.name), 1); + var player = { u: true, is_player: true, humanoid: true, secret: socket.observer_secret }; + for (prop in result.character) { + player[prop] = result.character[prop]; + } + if (!instances[player.map] || !instances[player.map].allow || instances[player.map].mount) { + var place = G.maps[player.map].on_exit || G.maps[B.start_map].on_exit || ["main", 0]; + player.map = player.in = place[0]; + player.x = G.maps[player.map].spawns[place[1]][0]; + player.y = G.maps[player.map].spawns[place[1]][1]; + } else { + player.in = player.map; + } + player.owner = data.user; + player.auth = data.user + "-" + data.auth; + player.last_sync = new Date(); + player.socket = socket; + player.max_stats = result.stats; + + if (data.bot == variables.bot_key) { + player.bot = true; + player.afk = "bot"; + } + if (data.no_html) { + player.afk = "code"; + try { + player.controller = (name_to_id[data.no_html] && data.no_html) || ""; + } catch (e) { + player.controller = ""; + } + } + if (!player.afk) { + player.afk = true; + } + if (gameplay == "test") { + player.name += parseInt(Math.random() * 10000); + } + player.real_id = player.id; + player.id = player.name; + + player.total_ips = 1; + player.width = 26; + player.height = 36; + player.damage_type = G.classes[player.type].damage_type; + player.xrange = 25; + player.red_zone = 0; + player.targets = player.targets_p = player.targets_m = player.targets_u = 0; + player.cid = 1; + player.hits = 0; + player.kills = 0; + player.m = 0; // map number + /* party variables*/ + player.pdps = 0; + player.party_length = 1; + player.party_luck = 0; + player.party_xp = 0; + player.party_gold = 0; + player.share = 0.1; + player.cx = player.cx || {}; + if (!player.s) { + player.s = {}; + } + player.t = { mdamage: 0, cgold: 0, dgold: 0, xp: 0, start: new Date() }; + player.hitchhikers = []; // socket events to be registered after a resend + player.last = { attack: future_ms(-1200), attacked: really_old }; + player.bets = {}; + player.base = dbase; + player.age = parseInt(ceil(hsince(new Date(player.created)) / 24.0)); + // player.vision=[round((data.width/2)/data.scale)+B.ext_vision,round((data.height/2)/data.scale)+B.ext_vision]; + // player.vision[0]=min(1000,player.vision[0]); + // player.vision[1]=min(700,player.vision[1]); + player.vision = B.vision; + + if (!player.verified) { + player.s.notverified = { ms: 30 * 60 * 1000 }; + } else if (player.s.notverified) { + player.s.notverified = { ms: 100 }; + } + + if (player.guild) { + console.log(player.guild); + player.guild = player.guild.short; + } + + if (gameplay == "hardcore") { + reset_player(player); + } // || gameplay=="test" + + init_player(player); + if (!observers[socket.id]) { + // observer hang up before "auth" + server_log("Abrupt stop for " + result.character.name, 1); + if (gameplay != "hardcore" && gameplay != "test") { + dc_players[player.real_id] = player; + } + sync_loop(); + return; + } + try { + delete_observer(socket); + } catch (e) {} + + players[socket.id] = player; + resume_instance(instances[player.in]); + instances[player.in].players[player.id] = player; + pmap_add(player); + + name_to_id[player.name] = socket.id; + id_to_id[player.id] = socket.id; + + cache_player_items(player); + invincible_logic(player); + serverhop_logic(player); + calculate_player_stats(player); + + if (data.epl == "mas" && data.receipt) { + player.platform = "mas"; + verify_mas_receipt(player, data.receipt); + } else if (data.epl == "steam" && data.ticket) { + player.platform = "steam"; + verify_steam_ticket(player, data.ticket); + } else { + player.platform = "web"; + if (result.character.pid) { + player.auth_id = result.character.pid; + } // part of the new restriction system [02/05/19] + } + + if (mode.drm_check) { + if (result.character.drm && !player.auth_id) { + player.s.authfail = { ms: 900000 * 1000 }; + } else if (player.s.authfail) { + player.s.authfail = { ms: 100 }; + } + } + + if (!is_player_allowed(player)) { + socket.emit("disconnect_reason", "limits"); + socket.disconnect(); + } else { + var cdata = player_to_client(player); + player.ipass = cdata.ipass = randomStr(12); + player.last_ipass = new Date(); + player.last.attack = future_ms(-10000); + player.last.transport = future_ms(-10000); + cdata.home = player.p.home; + cdata.friends = player.friends; + cdata.acx = player.p.acx; + cdata.xcx = player.p.xcx; + cdata.emx = player.p.emx; + cdata.info = instances[player.in].info; + cdata.base_gold = D.base_gold; + broadcast_e(true); + cdata.s_info = E; + if (result.code) { + cdata.code = result.code; + cdata.code_slot = result.code_slot; + cdata.code_version = result.code_version; + } + cdata.entities = send_all_xy(player, { raw: true }); + socket.emit("start", cdata); + total_players++; + } + }, + function (err) { + server_log("start_character_failed: " + data.character + " reason: " + err, 1); + if (observers[socket.id]) { + observers[socket.id].auth_engaged = false; + } + }, + ); + }); + socket.on("use", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.item == "hp" || data.item == "mp") { + if (player.last.potion && mssince(player.last.potion) < 0) { + return fail_response("not_ready"); + } + player.last.potion = future_ms(4000); + if (data.item == "hp") { + player.hp += 50; + disappearing_text(socket, player, "+50", { color: "green", xy: 1, s: "hp", nohp: 1 }); + } + if (data.item == "mp") { + player.mp += 100; + disappearing_text(socket, player, "+100", { color: "#006AA9", xy: 1, s: "mp", nomp: 1 }); + } + player.hp = min(player.hp, player.max_hp); + player.mp = min(player.mp, player.max_mp); + // calculate_player_stats(player); [22/11/16] + player.cid++; + player.u = true; + socket.emit("player", player_to_client(player)); + socket.emit("eval", { code: "pot_timeout(4000)" }); + } + success_response({}); + }); + socket.on("friend", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + server_log("friend: " + JSON.stringify(data)); + if (data.event == "request") { + var friend = players[name_to_id[data.name || ""]]; + if (!friend) { + return fail_response("friend_rleft"); + } + if (in_arr(friend.owner, player.friends) || friend.owner == player.owner) { + return success_response("friend_already"); + } + requests[player.name + "-" + friend.name] = { a: player.owner, b: friend.owner }; + friend.socket.emit("friend", { event: "request", name: player.name }); + return success_response("friend_rsent"); + } + if (data.event == "accept") { + if (!requests[data.name + "-" + player.name]) { + return fail_response("friend_expired"); + } + appengine_call( + "set_friends", + { user1: requests[data.name + "-" + player.name].a, user2: requests[data.name + "-" + player.name].b }, + function (result) { + var player = players[socket.id]; + if (result.failed) { + if (player) { + socket.emit("game_response", { response: "friend_failed", reason: result.reason }); + } + return; + } + //var friend=players[name_to_id[data.name||""]]; + //if(player) + //if(friend) friend.emit("friend",{event:"accepted",name:player.name}); + }, + function () { + var player = players[socket.id]; + if (player) { + socket.emit("game_response", { response: "friend_failed", reason: "coms failure" }); + } + }, + ); + requests[data.name + "-" + player.name] = false; + } + if (data.event == "unfriend") { + appengine_call( + "not_friends", + { user1: player.owner, user2: data.name }, + function (result) { + var player = players[socket.id]; + if (result.failed) { + if (player) { + socket.emit("game_response", { response: "unfriend_failed", reason: result.reason }); + } + return; + } + }, + function () { + var player = players[socket.id]; + if (player) { + socket.emit("game_response", { response: "unfriend_failed", reason: "coms failure" }); + } + }, + ); + } + success_response({ success: false, in_progress: true }); + }); + socket.on("duel", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.event == "challenge") { + var invited = players[name_to_id[data.id || data.name]]; + if (!invited || invited.id == player.id) { + return socket.emit("game_log", "Invalid"); + } + if (invited.duel || player.duel) { + return socket.emit("game_log", "Already dueling"); + } + invited.socket.emit("duel", { event: "chellenge", name: player.name }); + socket.emit("game_response", { response: "challenge_sent", name: invited.name }); + invited.socket.emit("game_response", { response: "challenge_received", name: player.name }); + challenges[player.name] = invited.name; + } else if (data.event == "accept") { + var challenger = players[name_to_id[data.id || data.name]]; + if (!challenger || challenges[challenger.name] != player.name) { + return socket.emit("game_log", "Challenge expired"); + } + if (challenger.duel || player.duel) { + return socket.emit("game_log", "Already dueling"); + } + if (is_in_pvp(challenger) || is_in_pvp(player)) { + return socket.emit("game_log", "Can't start a duel if any of the parties are already in a pvp zone"); + } + delete challenges[challenger.name]; + challenger.socket.emit("game_response", { response: "challenge_accepted", name: player.name }); + var name = randomStr(20); + var a = []; + var b = []; + instance = create_instance(name, "duelland"); + instance.info = { + seconds: (is_sdk && 20) || 60, + active: false, + A: [player_to_summary(challenger)], + B: [player_to_summary(player)], + id: name, + }; + instance.info.A[0].active = true; + instance.info.B[0].active = true; + clean_slate(challenger); + transport_player_to(challenger, name, 1); + if (challenger.party) { + parties[challenger.party].forEach(function (p) { + a.push(p); + }); + } else { + a = [challenger.name]; + } + clean_slate(player); + transport_player_to(player, name, 2); + if (player.party) { + parties[player.party].forEach(function (p) { + b.push(p); + }); + } else { + b = [player.name]; + } + if (!E.duels) { + E.duels = {}; + } + var duel = { + challenger: challenger.name, + a: a, + vs: player.name, + b: b, + instance: name, + seconds: (is_sdk && 20) || 60, + active: false, + id: name, + }; + challenger.team = "A"; + challenger.duel = duel; + challenger.s.stunned = { ms: 120000 }; + resend(challenger, "u+cid"); + player.team = "B"; + player.duel = duel; + player.s.stunned = { ms: 120000 }; + resend(player, "u+cid"); + E.duels[name] = duel; + broadcast_e(); + a.forEach(function (p) { + if (p != challenger.name && get_player(p)) { + get_player(p).socket.emit("game_response", { + response: "duel_started", + challenger: challenger.name, + vs: player.name, + id: name, + }); } }); - parties[player.party].forEach(function(name){ - var current=players[name_to_id[name]],cgold=round(chest.gold*(current.share||0)*r.goldm)+round((chest.egold||0)*(current.share||0)); - r.gold=cgold=server_tax(cgold); - current.gold+=cgold; - if(current.t) current.t.cgold+=cgold; - if(cgold) current.socket.emit("game_log",{message:to_pretty_num(cgold)+" gold",color:"gold"}); - if(current.in==player.in) current.socket.emit("disappearing_text",{"message":"+"+cgold,"x":chest.x,"y":chest.y-10,"args":{color:"gold","size":"large"}}); - resend(current,reopen[current.id]&&"reopen+nc+inv"||""); - current.socket.emit("chest_opened",r); + b.forEach(function (p) { + if (p != player.name && get_player(p)) { + get_player(p).socket.emit("game_response", { + response: "duel_started", + challenger: challenger.name, + vs: player.name, + id: name, + }); + } }); + } else if (data.event == "enter") { + if (is_in_pvp(player)) { + return socket.emit("game_log", "Can't join the duel from a pvp zone!"); + } + if (player.duel) { + return socket.emit("game_log", "Already in a duel!"); + } + if (!E.duels[data.id]) { + return socket.emit("game_log", "Duel expired"); + } + if (E.duels[data.id].active) { + return socket.emit("game_log", "Duel already started"); + } + if (!(E.duels[data.id].a.includes(player.name) || E.duels[data.id].b.includes(player.name))) { + return socket.emit("game_log", "Not your duel"); + } + clean_slate(player); + + if (E.duels[data.id].a.includes(player.name)) { + transport_player_to(player, data.id, 1); + player.team = "A"; + } else { + transport_player_to(player, data.id, 2); + player.team = "B"; + } + + player.duel = E.duels[data.id]; + if (player.duel.a.includes(player.name)) { + instances[data.id].info.A.push(player_to_summary(player)); + instances[data.id].info.A[instances[data.id].info.A.length - 1].active = true; + } else { + instances[data.id].info.B.push(player_to_summary(player)); + instances[data.id].info.B[instances[data.id].info.B.length - 1].active = true; + } + player.s.stunned = { ms: 120000 }; + + resend(player, "u+cid"); + instance_emit(data.id, "game_chat", { message: player.name + " joined the duel!" }); } - else - { - socket.emit("chest_opened",{id:data.id,gone:true}); - } - } - catch(e) - { - delete chests[data.id]; // If this didn't exist, any exception would end up being a source for infinite gold and items - log_trace("#X chest_error",e); - return fail_response("error"); - } - }) - socket.on('auth',function(data){ - if(gameplay=="test" && data.passphrase!="potato salad") return socket.emit("game_log","Wrong passphrase!"); - if(observers[socket.id] && observers[socket.id].auth_engaged) return socket.emit("game_log","Authorization in progress."); - if(dc_players[data.character]) return socket.emit("game_log","Authorization in progress."); - if(!server.live || !observers[socket.id] || players[socket.id]) return; - if(Object.keys(players).length>=max_players) - { - socket.emit("game_error","Can't accept more than "+max_players+" players at this time"); - return; - } - socket.observer_secret=randomStr(24); - observers[socket.id].auth_engaged=true; - appengine_call("start_character",{auth:data.user+'-'+data.auth,secret:socket.observer_secret,code_slot:data.code_slot,character:data.character,mode:gameplay,ip:get_ip(socket),suffix:"/"+data.character},function(result){ - if(observers[socket.id]) observers[socket.id].auth_engaged=false; - if(result.failed){ - socket.emit("game_error","Failed: "+result.reason); + }); + socket.on("party", function (data) { + var player = players[socket.id]; + if (!player) { return; } - // console.log(JSON.stringify(result)); - server_log('start_character: '+JSON.stringify(result.character.name),1); - var player={u:true,is_player:true,humanoid:true,secret:socket.observer_secret}; - for(prop in result.character) player[prop]=result.character[prop]; - if(!instances[player.map] || !instances[player.map].allow || instances[player.map].mount) - { - var place=G.maps[player.map].on_exit||G.maps[B.start_map].on_exit||["main",0]; - player.map=player.in=place[0]; - player.x=G.maps[player.map].spawns[place[1]][0]; - player.y=G.maps[player.map].spawns[place[1]][1]; - } - else player.in=player.map; - player.owner=data.user; - player.auth=data.user+'-'+data.auth; - player.last_sync=new Date(); - player.socket=socket; - player.max_stats=result.stats; - - if(data.bot==variables.bot_key) player.bot=true,player.afk="bot"; - if(data.no_html) - { - player.afk="code"; - try{player.controller=name_to_id[data.no_html]&&data.no_html||"";}catch(e){player.controller="";} - } - if(!player.afk) player.afk=true; - if(gameplay=="test") player.name+=parseInt(Math.random()*10000); - player.real_id=player.id; player.id=player.name; - - player.total_ips=1; - player.width=26; - player.height=36; - player.damage_type=G.classes[player.type].damage_type; - player.xrange=25; - player.red_zone=0; - player.targets=player.targets_p=player.targets_m=player.targets_u=0; - player.cid=1; - player.hits=0; player.kills=0; - player.m=0; // map number - /* party variables*/ - player.pdps=0; - player.party_length=1; - player.party_luck=0; - player.party_xp=0; - player.party_gold=0; - player.share=0.1; - player.cx=player.cx||{}; - if(!player.s) player.s={}; - player.t={mdamage:0,cgold:0,dgold:0,xp:0,start:new Date()}; - player.hitchhikers=[]; // socket events to be registered after a resend - player.last={attack:future_ms(-1200),attacked:really_old}; - player.bets={}; - player.base=dbase; - player.age=parseInt(ceil(hsince(new Date(player.created))/24.0)); - // player.vision=[round((data.width/2)/data.scale)+B.ext_vision,round((data.height/2)/data.scale)+B.ext_vision]; - // player.vision[0]=min(1000,player.vision[0]); - // player.vision[1]=min(700,player.vision[1]); - player.vision=B.vision; - - if(!player.verified) player.s.notverified={ms:30*60*1000}; - else if(player.s.notverified) player.s.notverified={ms:100}; - - if(player.guild) - { - console.log(player.guild); - player.guild=player.guild.short; - } - - if(gameplay=="hardcore") reset_player(player); // || gameplay=="test" - - init_player(player); - if(!observers[socket.id]) // observer hang up before "auth" - { - server_log("Abrupt stop for "+result.character.name,1); - if(gameplay!="hardcore" && gameplay!="test") dc_players[player.real_id]=player; - sync_loop(); - return; - } - try{delete_observer(socket)}catch(e){} - - players[socket.id]=player; - resume_instance(instances[player.in]); - instances[player.in].players[player.id]=player; pmap_add(player); - - name_to_id[player.name]=socket.id; - id_to_id[player.id]=socket.id; - - cache_player_items(player); - invincible_logic(player); - serverhop_logic(player); - calculate_player_stats(player); - - if(data.epl=="mas" && data.receipt) - { - player.platform="mas"; - verify_mas_receipt(player,data.receipt); - } - else if(data.epl=="steam" && data.ticket) - { - player.platform="steam"; - verify_steam_ticket(player,data.ticket); - } - else - { - player.platform="web"; - if(result.character.pid) player.auth_id=result.character.pid; // part of the new restriction system [02/05/19] - } - - if(mode.drm_check) - { - if(result.character.drm && !player.auth_id) player.s.authfail={ms:900000*1000}; - else if(player.s.authfail) player.s.authfail={ms:100}; - } - - if(!is_player_allowed(player)) - { - socket.emit("disconnect_reason","limits"); - socket.disconnect(); - } - else - { - var cdata=player_to_client(player); - player.ipass=cdata.ipass=randomStr(12); - player.last_ipass=new Date(); - player.last.attack=future_ms(-10000); - player.last.transport=future_ms(-10000); - cdata.home=player.p.home; - cdata.friends=player.friends; - cdata.acx=player.p.acx; - cdata.xcx=player.p.xcx; - cdata.emx=player.p.emx; - cdata.info=instances[player.in].info; - cdata.base_gold=D.base_gold; - broadcast_e(true); - cdata.s_info=E; - if(result.code) - { - cdata.code=result.code; - cdata.code_slot=result.code_slot; - cdata.code_version=result.code_version; + if (data.event == "invite") { + // if(player.party && player.party!=player.name) { socket.emit("game_log","Only the party leader can send invites"); return; } + if (player.party && (parties[player.party].length >= limits.party_max || player.party_length >= limits.party)) { + return fail_response("party_full"); } - cdata.entities=send_all_xy(player,{raw:true}); - socket.emit('start',cdata); - total_players++; - } - - },function(err){ - server_log('start_character_failed: '+data.character+" reason: "+err,1); - if(observers[socket.id]) observers[socket.id].auth_engaged=false; - }) - }); - socket.on('use',function(data){ - var player=players[socket.id]; if(!player) return; - if(data.item=="hp" || data.item=="mp") - { - if(player.last.potion && mssince(player.last.potion)<0) return fail_response("not_ready"); - player.last.potion=future_ms(4000); - if(data.item=="hp") player.hp+=50,disappearing_text(socket,player,"+50",{color:"green",xy:1,s:"hp",nohp:1}); - if(data.item=="mp") player.mp+=100,disappearing_text(socket,player,"+100",{color:"#006AA9",xy:1,s:"mp",nomp:1}); - player.hp=min(player.hp,player.max_hp); player.mp=min(player.mp,player.max_mp); - // calculate_player_stats(player); [22/11/16] - player.cid++; player.u=true; - socket.emit("player",player_to_client(player)); - socket.emit("eval",{code:"pot_timeout(4000)"}); - } - success_response({}) - }); - socket.on('friend',function(data){ - var player=players[socket.id]; - if(!player) return; - server_log("friend: "+JSON.stringify(data)); - if(data.event=="request") - { - var friend=players[name_to_id[data.name||""]]; - if(!friend) return fail_response("friend_rleft"); - if(in_arr(friend.owner,player.friends) || friend.owner==player.owner) return success_response("friend_already"); - requests[player.name+"-"+friend.name]={"a":player.owner,"b":friend.owner}; - friend.socket.emit("friend",{event:"request",name:player.name}); - return success_response("friend_rsent"); - } - if(data.event=="accept") - { - if(!requests[data.name+"-"+player.name]) return fail_response("friend_expired"); - appengine_call("set_friends",{user1:requests[data.name+"-"+player.name].a,user2:requests[data.name+"-"+player.name].b},function(result){ - var player=players[socket.id]; - if(result.failed) - { - if(player) socket.emit("game_response",{response:"friend_failed",reason:result.reason}); - return; + var invited = players[name_to_id[data.id || data.name]]; + if (!invited || invited.id == player.id) { + return fail_response("invalid"); } - //var friend=players[name_to_id[data.name||""]]; - //if(player) - //if(friend) friend.emit("friend",{event:"accepted",name:player.name}); - },function(){ - var player=players[socket.id]; - if(player) socket.emit("game_response",{response:"friend_failed",reason:"coms failure"}); - }); - requests[data.name+"-"+player.name]=false; - } - if(data.event=="unfriend") - { - appengine_call("not_friends",{user1:player.owner,user2:data.name},function(result){ - var player=players[socket.id]; - if(result.failed) - { - if(player) socket.emit("game_response",{response:"unfriend_failed",reason:result.reason}); - return; + if (player.party && player.party == invited.party) { + return success_response("already_in_party"); } - },function(){ - var player=players[socket.id]; - if(player) socket.emit("game_response",{response:"unfriend_failed",reason:"coms failure"}); - }); - } - success_response({success:false,in_progress:true}); - }); - socket.on('duel',function(data){ - var player=players[socket.id]; - if(!player) return; - if(data.event=="challenge") - { - var invited=players[name_to_id[data.id||data.name]]; - if(!invited || invited.id==player.id) return socket.emit("game_log","Invalid"); - if(invited.duel || player.duel) return socket.emit("game_log","Already dueling"); - invited.socket.emit("duel",{event:"chellenge",name:player.name}); - socket.emit("game_response",{"response":"challenge_sent",name:invited.name}); - invited.socket.emit("game_response",{"response":"challenge_received",name:player.name}); - challenges[player.name]=invited.name; - } - else if(data.event=="accept") - { - var challenger=players[name_to_id[data.id||data.name]]; - if(!challenger || challenges[challenger.name]!=player.name) return socket.emit("game_log","Challenge expired"); - if(challenger.duel || player.duel) return socket.emit("game_log","Already dueling"); - if(is_in_pvp(challenger) || is_in_pvp(player)) return socket.emit("game_log","Can't start a duel if any of the parties are already in a pvp zone"); - delete challenges[challenger.name]; - challenger.socket.emit("game_response",{"response":"challenge_accepted",name:player.name}); - var name=randomStr(20),a=[],b=[]; - instance=create_instance(name,"duelland"); - instance.info={"seconds":is_sdk&&20||60,"active":false,"A":[player_to_summary(challenger)],"B":[player_to_summary(player)],"id":name}; - instance.info.A[0].active=true; - instance.info.B[0].active=true; - clean_slate(challenger); transport_player_to(challenger,name,1); if(challenger.party) parties[challenger.party].forEach(function(p){a.push(p)}); else a=[challenger.name]; - clean_slate(player); transport_player_to(player,name,2); if(player.party) parties[player.party].forEach(function(p){b.push(p)}); else b=[player.name]; - if(!E.duels) E.duels={}; - var duel={"challenger":challenger.name,a:a,"vs":player.name,b:b,"instance":name,"seconds":is_sdk&&20||60,"active":false,"id":name}; - challenger.team="A"; challenger.duel=duel; challenger.s.stunned={ms:120000}; resend(challenger,"u+cid"); - player.team="B"; player.duel=duel; player.s.stunned={ms:120000}; resend(player,"u+cid"); - E.duels[name]=duel; - broadcast_e(); - a.forEach(function(p){ if(p!=challenger.name && get_player(p)) get_player(p).socket.emit("game_response",{response:"duel_started",challenger:challenger.name,vs:player.name,id:name}); }); - b.forEach(function(p){ if(p!=player.name && get_player(p)) get_player(p).socket.emit("game_response",{response:"duel_started",challenger:challenger.name,vs:player.name,id:name}); }); - } - else if(data.event=="enter") - { - if(is_in_pvp(player)) return socket.emit("game_log","Can't join the duel from a pvp zone!"); - if(player.duel) return socket.emit("game_log","Already in a duel!"); - if(!E.duels[data.id]) return socket.emit("game_log","Duel expired"); - if(E.duels[data.id].active) return socket.emit("game_log","Duel already started"); - if(!(E.duels[data.id].a.includes(player.name) || E.duels[data.id].b.includes(player.name))) return socket.emit("game_log","Not your duel"); - clean_slate(player); - - if(E.duels[data.id].a.includes(player.name)) transport_player_to(player,data.id,1),player.team="A"; - else transport_player_to(player,data.id,2),player.team="B"; - - player.duel=E.duels[data.id]; - if(player.duel.a.includes(player.name)) instances[data.id].info.A.push(player_to_summary(player)),instances[data.id].info.A[instances[data.id].info.A.length-1].active=true; - else instances[data.id].info.B.push(player_to_summary(player)),instances[data.id].info.B[instances[data.id].info.B.length-1].active=true; - player.s.stunned={ms:120000}; - - resend(player,"u+cid"); - instance_emit(data.id,"game_chat",{message:player.name+" joined the duel!"}); - } - }); - socket.on('party',function(data){ - var player=players[socket.id]; if(!player) return; - if(data.event=="invite") - { - // if(player.party && player.party!=player.name) { socket.emit("game_log","Only the party leader can send invites"); return; } - if(player.party && (parties[player.party].length>=limits.party_max || player.party_length>=limits.party)) return fail_response("party_full"); - var invited=players[name_to_id[data.id||data.name]]; - if(!invited || invited.id==player.id) return fail_response("invalid"); - if(player.party && player.party==invited.party) return success_response("already_in_party"); - // if(invited.party) { socket.emit("game_log",invited.name+" is already partying"); return; } - invited.socket.emit("invite",{name:player.name}); - socket.emit("game_log","Invited "+invited.name+" to party"); - if(!invitations[player.name]) invitations[player.name]={}; - invitations[player.name][invited.id]=1; - } - if(data.event=="request") - { - // if(player.party) { return; } - var invited=players[name_to_id[data.id||data.name]]; - if(!invited || invited.id==player.id) return fail_response("invalid"); - if(player.party && player.party==invited.party) return success_response("already_in_party"); - // if(!invited.party) { socket.emit("game_log",invited.name+" isn't partying"); return; } - invited.socket.emit("request",{name:player.name}); - socket.emit("game_log","Requested to join "+invited.name+"'s party"); - if(!requests[player.name]) requests[player.name]={}; - requests[player.name][invited.id]=1; - } - if(data.event=="accept") - { - var inviter=players[name_to_id[data.name]]; - // if(player.party) { socket.emit("party_update",{list:parties[player.party],party:party_to_client(player.party)}); socket.emit("game_log","Already partying"); return; } - // if(!inviter || (inviter.party && inviter.party!=inviter.name)) { socket.emit("game_log","Party was disbanded"); return; } - if(!inviter) return fail_response("player_gone",{name:data.name}) - if(inviter.party && (parties[inviter.party].length>=limits.party_max || inviter.party_length>=limits.party)) return fail_response("party_full"); - if(!invitations[inviter.name] || !invitations[inviter.name][player.id]) return fail_response("invitation_expired"); - if(player.party && player.party==inviter.party) return success_response("already_in_party"); - if(player.party) - { - leave_party(player.party,player); - socket.emit("party_update",{}); - socket.emit("game_log","Left your current party"); + // if(invited.party) { socket.emit("game_log",invited.name+" is already partying"); return; } + invited.socket.emit("invite", { name: player.name }); + socket.emit("game_log", "Invited " + invited.name + " to party"); + if (!invitations[player.name]) { + invitations[player.name] = {}; + } + invitations[player.name][invited.id] = 1; } - invitations[inviter.name][player.id]=0; - if(!inviter.party) - { - inviter.party=inviter.name; - parties[inviter.name]=[inviter.name]; - resend(inviter,"nc+u+cid"); - delete requests[inviter.name]; - if(!players[name_to_id[data.name]]) return fail_response("player_gone",{name:data.name}); // these repetitions are all because socket.emit's can cause in-line disconnects and disband parties right after creation [07/08/20] + if (data.event == "request") { + // if(player.party) { return; } + var invited = players[name_to_id[data.id || data.name]]; + if (!invited || invited.id == player.id) { + return fail_response("invalid"); + } + if (player.party && player.party == invited.party) { + return success_response("already_in_party"); + } + // if(!invited.party) { socket.emit("game_log",invited.name+" isn't partying"); return; } + invited.socket.emit("request", { name: player.name }); + socket.emit("game_log", "Requested to join " + invited.name + "'s party"); + if (!requests[player.name]) { + requests[player.name] = {}; + } + requests[player.name][invited.id] = 1; } - player.party=inviter.party; - parties[inviter.party].push(player.name); - party_emit(player.party,"party_update",{list:parties[inviter.party],party:party_to_client(inviter.party),message:player.name+" joined the party"+(inviter.party!=inviter.name && " with "+inviter.name+"'s invite" || "")}); - resend(player,"nc+u+cid"); - delete requests[player.name]; - delete requests[inviter.name]; // optional - } - if(data.event=="raccept") - { - var requester=players[name_to_id[data.name]]; - if(!requester) return fail_response("player_gone",{name:data.name}); - // if(requester.party) { socket.emit("game_log","Already partying"); return; } - if(player.party && (parties[player.party].length>=limits.party_max || player.party_length>=limits.party)) return fail_response("party_full"); - if(!requests[requester.name] || !requests[requester.name][player.id]) return fail_response("request_expired"); - if(player.party && player.party==requester.party) return success_response("already_in_party"); - if(requester.party) - { - leave_party(requester.party,requester); - requester.socket.emit("party_update",{}); - requester.socket.emit("game_log","Left the party"); - if(!players[name_to_id[data.name]]) return fail_response("player_gone",{name:data.name}); - } - requests[requester.name][player.id]=0; - if(!player.party) - { - player.party=player.name; - parties[player.name]=[player.name]; - resend(player,"nc+u+cid"); + if (data.event == "accept") { + var inviter = players[name_to_id[data.name]]; + // if(player.party) { socket.emit("party_update",{list:parties[player.party],party:party_to_client(player.party)}); socket.emit("game_log","Already partying"); return; } + // if(!inviter || (inviter.party && inviter.party!=inviter.name)) { socket.emit("game_log","Party was disbanded"); return; } + if (!inviter) { + return fail_response("player_gone", { name: data.name }); + } + if ( + inviter.party && + (parties[inviter.party].length >= limits.party_max || inviter.party_length >= limits.party) + ) { + return fail_response("party_full"); + } + if (!invitations[inviter.name] || !invitations[inviter.name][player.id]) { + return fail_response("invitation_expired"); + } + if (player.party && player.party == inviter.party) { + return success_response("already_in_party"); + } + if (player.party) { + leave_party(player.party, player); + socket.emit("party_update", {}); + socket.emit("game_log", "Left your current party"); + } + invitations[inviter.name][player.id] = 0; + if (!inviter.party) { + inviter.party = inviter.name; + parties[inviter.name] = [inviter.name]; + resend(inviter, "nc+u+cid"); + delete requests[inviter.name]; + if (!players[name_to_id[data.name]]) { + return fail_response("player_gone", { name: data.name }); + } // these repetitions are all because socket.emit's can cause in-line disconnects and disband parties right after creation [07/08/20] + } + player.party = inviter.party; + parties[inviter.party].push(player.name); + party_emit(player.party, "party_update", { + list: parties[inviter.party], + party: party_to_client(inviter.party), + message: + player.name + + " joined the party" + + ((inviter.party != inviter.name && " with " + inviter.name + "'s invite") || ""), + }); + resend(player, "nc+u+cid"); delete requests[player.name]; + delete requests[inviter.name]; // optional } - requester.party=player.party; - parties[player.party].push(requester.name); - party_emit(requester.party,"party_update",{list:parties[player.party],party:party_to_client(player.party),message:requester.name+" joined the party"}); - resend(requester,"nc+u+cid"); - delete requests[requester.name]; - delete requests[player.name]; // optional - } - if(data.event=="leave") - { - if(!player.party) { socket.emit("party_update",{}); return success_response({}); } - leave_party(player.party,player); - socket.emit("party_update",{}); - socket.emit("game_log","Left the party"); - resend(player,"nc+u+cid"); - } - if(data.event=="kick") - { - if(!player.party) return success_response({}); - // if(player.party && player.party!=player.name) { socket.emit("game_log","Only the party leader can kick someone"); return; } - if(!in_arr(data.name,parties[player.party])) { socket.emit("party_update",{list:parties[player.party],party:party_to_client(player.party)}); return success_response({}); } - if(parties[player.party].indexOf(player.name)>parties[player.party].indexOf(data.name)) return fail_response("cant_kick"); - var kicked=players[name_to_id[data.name]]; - if(!kicked) return fail_response("player_gone"); - leave_party(player.party,kicked); - if(player.party) party_emit(player.party,"game_log",player.name+" kicked "+kicked.name); - kicked.socket.emit("party_update",{}); - kicked.socket.emit("game_log","You've been removed from the party"); - resend(kicked,"nc+u+cid"); - } - success_response({}); - }); - socket.on('magiport',function(data){ - var player=players[socket.id]; if(!player) return; - if(magiportations[data.name] && magiportations[data.name][player.name] && get_player(data.name)) - { - delete magiportations[data.name][player.name]; - if(instances[get_player(data.name).in].mount!=instances[player.in].mount) return fail_response("cant_in_bank"); - if(!magiport_someone(player,get_player(data.name))) return fail_response("invalid"); - return success_response({}); - } - else - { - player.socket.emit("game_response",{response:"magiport_gone",name:data.name}); - return fail_response("inviter_gone"); - } - }); - socket.on('trade',function(data){ - var player=players[socket.id]; - if(!player) return; - if(data.event=="show" && player.p.trades!=1) - { - player.p.trades=1; - reslot_player(player); - resend(player,"nc+u+cid"); - } - else if(data.event=="hide" && player.p.trades!=null) - { - player.p.trades=null; - reslot_player(player); - resend(player,"nc+u+cid"); - } - }); - socket.on('signup',function(data){ - var player=players[socket.id]; if(!player) return; - if(!signups[player.name]) disappearing_text(false_socket,npcs.bean,"+1",{color:"#4D9A59",xy:1}); - signups[player.name]=true; - socket.emit("game_response",{response:"signed_up"}); - }); - socket.on('join',function(data){ - var player=players[socket.id]; if(!player) return; - if(player.type=="merchant") return fail_response("no_merchants"); - if(player.s.hopsickness) return fail_response("cant_when_sick"); - if(player.user) return fail_response("cant_in_bank"); - if(data.name=="goobrawl" && events.goobrawl) - { - if(player.map!="goobrawl") transport_player_to(player,"goobrawl"); - } - else if(data.name=="crabxx" && events.crabxx) - { - if(simple_distance(player,{map:"main",x:-1000,y:1700})>200) transport_player_to(player,"main",[-1000,1700,0,40]); - } - else if(data.name=="franky" && events.franky) - { - if(simple_distance(player,{map:"level2w",x:-300,y:150})>200) transport_player_to(player,"level2w",[-300,150,0,40]); - } - else if(data.name=="icegolem" && events.icegolem) - { - if(simple_distance(player,{map:"winterland",x:820,y:425})>100) transport_player_to(player,"winterland",[820,425,0,10]); - } - else if(data.name=="abtesting" && E.abtesting) - { - if(player.map!="abtesting" || !player.team) - { - if(ssince(timers.abtesting_start)>120 && (!player.p.abtesting || player.p.abtesting[0]!=E.abtesting.id)) - return fail_response("join_too_late"); - - if(player.p.abtesting && player.p.abtesting[0]==E.abtesting.id) player.team=player.p.abtesting[1]; - else player.team=random_one(["A","B"]); - - player.p.abtesting=[E.abtesting.id,player.team]; - - player.team=player.p.abtesting[1]; // transport does a restore ... - save_state(player); - - - if(player.team=="A") transport_player_to(player,"abtesting",2); - else if(player.team=="B") transport_player_to(player,"abtesting",3); - - resend(player,"cid"); - } - } - else - return fail_response("cant_join"); - success_response(); - }); - socket.on('stop',function(data){ - var player=players[socket.id],change=false; - if(!player) return; - if(!data || !data.action) - { - if(player.s.invis) step_out_of_invis(player),change=true; - if(Object.keys(player.c).length) player.c={},change=true; - } - else if(data.action=="invis") - { - if(player.s.invis) step_out_of_invis(player),change=true; - } - else if(data.action=="channeling") - { - if(Object.keys(player.c).length) player.c={},change=true; - } - else if(data.action=="teleport" || data.action=="town") - { - if(player.c.town) { delete player.c.town; change=true;} - } - else if(data.action=="revival") - { - if(player.c.revival) { delete player.c.revival; change=true;} - } - if(change) resend(player,"u+cid"); - success_response(); - }); - socket.on('tarot',function(data){ - var player=players[socket.id]; if(!player) return; - var npc=G.maps[player.map].ref.twitch; - if(!npc || simple_distance(npc,player)>500) return socket.emit("game_response","distance"); - for(var name in player.s) if(name.startsWith("tarot")) return socket.emit("game_response","tarot_exists"); - - }); - socket.on('bet',function(data){ - if(!instances.tavern) return; - var player=players[socket.id]; - if(!player || player.user || player.map!="tavern" && !is_sdk) return; - if(player.s.xshotted) return socket.emit("game_response","bet_xshot"); - if(data.type=="roulette") - { - if(!is_sdk) return; // Old routine [23/08/18] - if(!data.odds) data.odds="black"; data.odds=""+data.odds; - data.gold=max(1,parseInt(data.gold)||1); - if(!tavern.roulette.odds[data.odds]) return; - if(tavern.roulette.state!="bets") { socket.emit("game_log","Not taking bets yet!"); return; } - if(Object.keys(player.bets).length>=5) { socket.emit("game_log","You already have 5 active bets"); return; } - if(player.goldplayer.gold) return socket.emit("game_response","gold_not_enough"); - if(win-edge-gold>(S.gold-house_debt())*0.40) return socket.emit("game_response","tavern_gold_not_enough"); - if(Object.keys(player.bets).length) return socket.emit("game_response","tavern_dice_exist"); - // if(Object.keys(player.bets).length>=5) return socket.emit("game_response","tavern_too_many_bets"); - // socket.emit("game_log",{"message":"Bet accepted on "+num.toFixed(2)+" "+dir.toUpperCase(),"color":"white"}); - socket.emit("game_log",{"message":"Num: "+num.toFixed(2)+" "+dir.toUpperCase(),"color":"white"}); - socket.emit("game_log",{"message":"Bet: "+to_pretty_num(gold)+" gold","color":"gray"}); - - var rid=randomStr(10); - player.gold-=gold; - player.bets[rid]={id:rid,type:"dice",num:num,gold:gold,dir:dir,pid:socket.id,state:"bet",win:win,edge:edge,odds:odds}; - tavern.dice.players[player.socket.id]=true; - instance_emit(tavern,"tavern",{event:"bet",name:player.name,type:"dice",num:num,gold:gold,dir:dir}); - resend(player,"reopen+nc"); - } - if(data.type=="slots") - { - var gold=1000000; - if(gold>player.gold) return socket.emit("game_response","gold_not_enough"); - player.gold-=gold; S.gold+=gold; - player.q.slots={ms:3000}; - xy_emit(player,"ui",{type:"slots",player:player.name}); - socket.emit("game_response",{response:"gold_use",gold:gold,game:data.type}); - resend(player,"u+cid+reopen+nc"); - } - }); - socket.on('tavern',function(data){ - if(data.event=="info") - { - socket.emit("tavern",{event:"info",edge:house_edge(),"max":parseInt((S.gold-house_debt())*0.40)}) - } - }); - socket.on('play',function(data){ - - }); - socket.on('pet',function(data){ - var player=players[socket.id]; if(!player) return; - if(!player.monster) player.monster=new_monster(player.in,{type:player.pet,stype:"pet",x:player.x,y:player.y,owner:player.name,name:"Skimpy"}); - }) - socket.on('whistle',function(data){ - if(!player.monster) return; - xy_emit(player.monster,"disappear",{id:player.monster.id}); - player.monster.x=player.x; - player.monster.y=player.y; - player.monster.map=player.map; - player.monster.in=player.in; - player.monster.u=true; - player.monster.cid++; - }); - socket.on('list_pvp',function(data){ - var plist=[]; - for(var j=1;j<201;j++) - { - if(pend-j<0) break; - plist.push(pwns[(pend-j)%200]); - } - socket.emit("pvp_list",{code:data && data.code,list:plist}) - }); - socket.on('players',function(data){ - var player=players[socket.id],sdata=[]; - if(!player) return; // || is_pvp - for(var id in players) - { - var current=players[id],mapn=current.map; - current.age=parseInt(ceil(hsince(new Date(current.created))/24.0)); - if(is_pvp) - { - mapn="Unknown"; - sdata.push({name:"Hidden",map:mapn,age:current.age,level:current.level,type:current.type,afk:current.afk&&1||0,party:current.party&&"Hidden"||'',kills:current.kills}); - } - else if(!is_in_pvp(current)) sdata.push({name:current.name,map:mapn,age:current.age,level:current.level,type:current.type,afk:current.afk&&1||0,party:current.party||''}); - } - socket.emit("players",sdata); - }); - socket.on('pets',function(data){ - var player=players[socket.id],sdata=[]; - if(!player) return; // || is_pvp - for(var id in player.p.pets||{}) - { - sdata.push(player.p.pets[id]); - } - socket.emit("players",sdata); - }); - socket.on('harakiri',function(data){ - var player=players[socket.id]; - if(!player || player.rip) return; - defeat_player(player); - if(!player.rip) rip(player); - disappearing_text(player.socket,player,"SEPPUKU",{xy:1,size:"huge",color:"#6F76A6"}); - resend(player,"u+cid"); - }); - socket.on('deepsea',function(data){ - return; - var player=players[socket.id]; - if(!player || player.rip || player.tskin=="deepsea") return; - player.tskin="deepsea"; - disappearing_text(player.socket,player,"ROARRRRRRR",{xy:1,size:"huge",color:"#60A975"}); - resend(player,"u+cid"); - }); - socket.on('blend',function(data){ - var player=players[socket.id],min=99999,x=null; - if(!player || player.rip) return; - for(var id in instances[player.in].monsters||{}) - { - var m=instances[player.in].monsters[id],c=distance(m,player,true); - if(c= limits.party_max || player.party_length >= limits.party)) { + return fail_response("party_full"); } - }catch(e){log_trace("#X DCERROR",e);} - } - if(observer) - { - server_log("Disconnected Observer: "+socket.id+" Calls: "+socket.total_calls); - try{ - delete_observer(socket); - }catch(e){log_trace("#X DCERRORO ",e);} - } - }); - socket.on('shutdown',function(data){ - if(data.pass!=variables.access_master) return; - if(data.reason) broadcast("disconnect_reason",data.reason); - setTimeout(shutdown_routine,10000); - }); - socket.on('notice',function(data){ - if(data.pass!=variables.access_master) return; - broadcast("notice",{message:data.message}); - }); - socket.on('render',function(data){ - if(data.pass!=variables.access_master) return; - var player=players[socket.id]; - var output="",json_output=undefined,window=null,after=""; - try{ - eval(data.code); - } - catch(e) - { - output="Exception: "+e; - } - if(window) socket.emit('simple_eval',{window:window,after:after,code:'for(var id in data.window) window[id]=data.window[id]; eval(data.after);'}); - else if(json_output!==undefined) socket.emit('simple_eval',{output:output,json_output:json_output,code:'show_json(data.json_output)'}); - else socket.emit('simple_eval',{output:output,json_output:json_output,code:'show_modal("
"+data.output+"
")'}); - resend(player,"reopen+u+cid"); - }); - socket.on('error',function(data){ - // socket.emit("error") brings server down [16/02/22] - }); - socket.on('eval',function(data){ - if(data.command) - { - var player=players[socket.id]; if(!player) return; - if(player.map!="cyberland" || player.rip) return player.socket.emit("game_log","Not connected to the mainframe"); - if(data.command=="hello") - { - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"hi",id:"mainframe"}); - } - else if(data.command=="give") - { - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"what?",id:"mainframe"}); - } - else if(data.command.startsWith("swap")) - { - var numbers=data.command.split(" "); - if(numbers.length!=3) - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"...",id:"mainframe"}); - else - { - var a=parseInt(numbers[1]),b=parseInt(numbers[2]); - if(0<=a && a<42 && 0<=b && b<42) - { - if(a==player.p.item_num) player.p.item_num=b; - else if(b==player.p.item_num) player.p.item_num=a; - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"done",id:"mainframe"}); - } - else - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"ugh, ok",id:"mainframe"}); + if (!requests[requester.name] || !requests[requester.name][player.id]) { + return fail_response("request_expired"); + } + if (player.party && player.party == requester.party) { + return success_response("already_in_party"); + } + if (requester.party) { + leave_party(requester.party, requester); + requester.socket.emit("party_update", {}); + requester.socket.emit("game_log", "Left the party"); + if (!players[name_to_id[data.name]]) { + return fail_response("player_gone", { name: data.name }); } + } + requests[requester.name][player.id] = 0; + if (!player.party) { + player.party = player.name; + parties[player.name] = [player.name]; + resend(player, "nc+u+cid"); + delete requests[player.name]; + } + requester.party = player.party; + parties[player.party].push(requester.name); + party_emit(requester.party, "party_update", { + list: parties[player.party], + party: party_to_client(player.party), + message: requester.name + " joined the party", + }); + resend(requester, "nc+u+cid"); + delete requests[requester.name]; + delete requests[player.name]; // optional } - else if(data.command=="stop") - { - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"mechagnomes assemble",id:"mainframe"}); - get_monsters("mechagnome").forEach(function(m){ - if(m.target) stop_pursuit(m,{stop:1,cause:"stop()"}); - //else m.irregular=3; + if (data.event == "leave") { + if (!player.party) { + socket.emit("party_update", {}); + return success_response({}); + } + leave_party(player.party, player); + socket.emit("party_update", {}); + socket.emit("game_log", "Left the party"); + resend(player, "nc+u+cid"); + } + if (data.event == "kick") { + if (!player.party) { + return success_response({}); + } + // if(player.party && player.party!=player.name) { socket.emit("game_log","Only the party leader can kick someone"); return; } + if (!in_arr(data.name, parties[player.party])) { + socket.emit("party_update", { list: parties[player.party], party: party_to_client(player.party) }); + return success_response({}); + } + if (parties[player.party].indexOf(player.name) > parties[player.party].indexOf(data.name)) { + return fail_response("cant_kick"); + } + var kicked = players[name_to_id[data.name]]; + if (!kicked) { + return fail_response("player_gone"); + } + leave_party(player.party, kicked); + if (player.party) { + party_emit(player.party, "game_log", player.name + " kicked " + kicked.name); + } + kicked.socket.emit("party_update", {}); + kicked.socket.emit("game_log", "You've been removed from the party"); + resend(kicked, "nc+u+cid"); + } + success_response({}); + }); + socket.on("magiport", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (magiportations[data.name] && magiportations[data.name][player.name] && get_player(data.name)) { + delete magiportations[data.name][player.name]; + if (instances[get_player(data.name).in].mount != instances[player.in].mount) { + return fail_response("cant_in_bank"); + } + if (!magiport_someone(player, get_player(data.name))) { + return fail_response("invalid"); + } + return success_response({}); + } else { + player.socket.emit("game_response", { response: "magiport_gone", name: data.name }); + return fail_response("inviter_gone"); + } + }); + socket.on("trade", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (data.event == "show" && player.p.trades != 1) { + player.p.trades = 1; + reslot_player(player); + resend(player, "nc+u+cid"); + } else if (data.event == "hide" && player.p.trades != null) { + player.p.trades = null; + reslot_player(player); + resend(player, "nc+u+cid"); + } + }); + socket.on("signup", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!signups[player.name]) { + disappearing_text(false_socket, npcs.bean, "+1", { color: "#4D9A59", xy: 1 }); + } + signups[player.name] = true; + socket.emit("game_response", { response: "signed_up" }); + }); + socket.on("join", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (player.type == "merchant") { + return fail_response("no_merchants"); + } + if (player.s.hopsickness) { + return fail_response("cant_when_sick"); + } + if (player.user) { + return fail_response("cant_in_bank"); + } + if (data.name == "goobrawl" && events.goobrawl) { + if (player.map != "goobrawl") { + transport_player_to(player, "goobrawl"); + } + } else if (data.name == "crabxx" && events.crabxx) { + if (simple_distance(player, { map: "main", x: -1000, y: 1700 }) > 200) { + transport_player_to(player, "main", [-1000, 1700, 0, 40]); + } + } else if (data.name == "franky" && events.franky) { + if (simple_distance(player, { map: "level2w", x: -300, y: 150 }) > 200) { + transport_player_to(player, "level2w", [-300, 150, 0, 40]); + } + } else if (data.name == "icegolem" && events.icegolem) { + if (simple_distance(player, { map: "winterland", x: 820, y: 425 }) > 100) { + transport_player_to(player, "winterland", [820, 425, 0, 10]); + } + } else if (data.name == "abtesting" && E.abtesting) { + if (player.map != "abtesting" || !player.team) { + if ( + ssince(timers.abtesting_start) > 120 && + (!player.p.abtesting || player.p.abtesting[0] != E.abtesting.id) + ) { + return fail_response("join_too_late"); + } + + if (player.p.abtesting && player.p.abtesting[0] == E.abtesting.id) { + player.team = player.p.abtesting[1]; + } else { + player.team = random_one(["A", "B"]); + } + + player.p.abtesting = [E.abtesting.id, player.team]; + + player.team = player.p.abtesting[1]; // transport does a restore ... + save_state(player); + + if (player.team == "A") { + transport_player_to(player, "abtesting", 2); + } else if (player.team == "B") { + transport_player_to(player, "abtesting", 3); + } + + resend(player, "cid"); + } + } else { + return fail_response("cant_join"); + } + success_response(); + }); + socket.on("stop", function (data) { + var player = players[socket.id]; + var change = false; + if (!player) { + return; + } + if (!data || !data.action) { + if (player.s.invis) { + step_out_of_invis(player); + change = true; + } + if (Object.keys(player.c).length) { + player.c = {}; + change = true; + } + } else if (data.action == "invis") { + if (player.s.invis) { + step_out_of_invis(player); + change = true; + } + } else if (data.action == "channeling") { + if (Object.keys(player.c).length) { + player.c = {}; + change = true; + } + } else if (data.action == "teleport" || data.action == "town") { + if (player.c.town) { + delete player.c.town; + change = true; + } + } else if (data.action == "revival") { + if (player.c.revival) { + delete player.c.revival; + change = true; + } + } + if (change) { + resend(player, "u+cid"); + } + success_response(); + }); + socket.on("tarot", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + var npc = G.maps[player.map].ref.twitch; + if (!npc || simple_distance(npc, player) > 500) { + return socket.emit("game_response", "distance"); + } + for (var name in player.s) { + if (name.startsWith("tarot")) { + return socket.emit("game_response", "tarot_exists"); + } + } + }); + socket.on("bet", function (data) { + if (!instances.tavern) { + return; + } + var player = players[socket.id]; + if (!player || player.user || (player.map != "tavern" && !is_sdk)) { + return; + } + if (player.s.xshotted) { + return socket.emit("game_response", "bet_xshot"); + } + if (data.type == "roulette") { + if (!is_sdk) { + return; + } // Old routine [23/08/18] + if (!data.odds) { + data.odds = "black"; + } + data.odds = "" + data.odds; + data.gold = max(1, parseInt(data.gold) || 1); + if (!tavern.roulette.odds[data.odds]) { + return; + } + if (tavern.roulette.state != "bets") { + socket.emit("game_log", "Not taking bets yet!"); + return; + } + if (Object.keys(player.bets).length >= 5) { + socket.emit("game_log", "You already have 5 active bets"); + return; + } + if (player.gold < data.gold) { + socket.emit("game_log", "Not enough gold"); + return; + } + + socket.emit("game_log", { message: "Bet accepted on " + data.odds, color: "white" }); + socket.emit("game_log", { message: to_pretty_num(data.gold) + " gold", color: "gray" }); + + var rid = randomStr(10); + player.gold -= data.gold; + player.bets[rid] = { + id: rid, + type: "roulette", + odds: data.odds, + gold: data.gold, + pid: socket.id, + state: "bet", + }; + tavern.roulette.players[player.socket.id] = true; + io.to("roulette").emit("bet", { name: player.name, type: "roulette", odds: data.odds, gold: data.gold }); + socket.emit("tavern", player.bets[rid]); + resend(player, "reopen+nc"); + } + if (data.type == "dice") { + // server_log(JSON.stringify(data)); + var num = min(99.99, max(parseFloat(data.num) || 0, 0.01)); + var gold = max(10000, min(parseInt(data.gold) || 0, 100000000000)); + var odds = 100.0 / num; + var dir = "down"; + if (data.dir == "up") { + odds = 100.0 / (100 - num); + dir = "up"; + } + odds = min(odds, 10000); + odds = parseFloat(floor_f2(odds)); + var win = gold * odds; + var raw = win; + var edge = ceil(((gold * odds - gold) * house_edge()) / 100.0); + win = parseInt(win); + edge = parseInt(edge); + if (tavern.dice.state == "roll") { + return socket.emit("game_response", "tavern_too_late"); + } + if (tavern.dice.state != "bets") { + return socket.emit("game_response", "tavern_not_yet"); + } + if (gold > player.gold) { + return socket.emit("game_response", "gold_not_enough"); + } + if (win - edge - gold > (S.gold - house_debt()) * 0.4) { + return socket.emit("game_response", "tavern_gold_not_enough"); + } + if (Object.keys(player.bets).length) { + return socket.emit("game_response", "tavern_dice_exist"); + } + // if(Object.keys(player.bets).length>=5) return socket.emit("game_response","tavern_too_many_bets"); + // socket.emit("game_log",{"message":"Bet accepted on "+num.toFixed(2)+" "+dir.toUpperCase(),"color":"white"}); + socket.emit("game_log", { message: "Num: " + num.toFixed(2) + " " + dir.toUpperCase(), color: "white" }); + socket.emit("game_log", { message: "Bet: " + to_pretty_num(gold) + " gold", color: "gray" }); + + var rid = randomStr(10); + player.gold -= gold; + player.bets[rid] = { + id: rid, + type: "dice", + num: num, + gold: gold, + dir: dir, + pid: socket.id, + state: "bet", + win: win, + edge: edge, + odds: odds, + }; + tavern.dice.players[player.socket.id] = true; + instance_emit(tavern, "tavern", { + event: "bet", + name: player.name, + type: "dice", + num: num, + gold: gold, + dir: dir, + }); + resend(player, "reopen+nc"); + } + if (data.type == "slots") { + var gold = 1000000; + if (gold > player.gold) { + return socket.emit("game_response", "gold_not_enough"); + } + player.gold -= gold; + S.gold += gold; + player.q.slots = { ms: 3000 }; + xy_emit(player, "ui", { type: "slots", player: player.name }); + socket.emit("game_response", { response: "gold_use", gold: gold, game: data.type }); + resend(player, "u+cid+reopen+nc"); + } + }); + socket.on("tavern", function (data) { + if (data.event == "info") { + socket.emit("tavern", { event: "info", edge: house_edge(), max: parseInt((S.gold - house_debt()) * 0.4) }); + } + }); + socket.on("play", function (data) {}); + socket.on("pet", function (data) { + var player = players[socket.id]; + if (!player) { + return; + } + if (!player.monster) { + player.monster = new_monster(player.in, { + type: player.pet, + stype: "pet", + x: player.x, + y: player.y, + owner: player.name, + name: "Skimpy", }); } - else if(data.command.startsWith("give") && data.command!="give spares") - { - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"no",id:"mainframe"}); + }); + socket.on("whistle", function (data) { + if (!player.monster) { + return; } - else if(data.command=="secret web mode" && (player.p.steam_id || player.p.mas_auth_id)) - { - player.p.secret_web_mode=true; - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"secret web mode unlocked",id:"mainframe"}); - } - else if(data.command=="give spares") - { - if(S.misc && S.misc.spares && S.misc.spares.length) - { - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"here you go",id:"mainframe"}); - drop_one_thing(player,S.misc.spares,{x:1,y:-88}); - S.misc.spares=[]; + xy_emit(player.monster, "disappear", { id: player.monster.id }); + player.monster.x = player.x; + player.monster.y = player.y; + player.monster.map = player.map; + player.monster.in = player.in; + player.monster.u = true; + player.monster.cid++; + }); + socket.on("list_pvp", function (data) { + var plist = []; + for (var j = 1; j < 201; j++) { + if (pend - j < 0) { + break; } - else - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"come later",id:"mainframe"}); + plist.push(pwns[(pend - j) % 200]); } - else - { - - if(!player.supercomputer) - { - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"UNAUTHORIZED COMMAND",id:"mainframe"}); - for(var id in instances.cyberland.monsters) - { - var monster=instances.cyberland.monsters[id]; - if(!monster.target) target_player(monster,player); + socket.emit("pvp_list", { code: data && data.code, list: plist }); + }); + socket.on("players", function (data) { + var player = players[socket.id]; + var sdata = []; + if (!player) { + return; + } // || is_pvp + for (var id in players) { + var current = players[id]; + var mapn = current.map; + current.age = parseInt(ceil(hsince(new Date(current.created)) / 24.0)); + if (is_pvp) { + mapn = "Unknown"; + sdata.push({ + name: "Hidden", + map: mapn, + age: current.age, + level: current.level, + type: current.type, + afk: (current.afk && 1) || 0, + party: (current.party && "Hidden") || "", + kills: current.kills, + }); + } else if (!is_in_pvp(current)) { + sdata.push({ + name: current.name, + map: mapn, + age: current.age, + level: current.level, + type: current.type, + afk: (current.afk && 1) || 0, + party: current.party || "", + }); + } + } + socket.emit("players", sdata); + }); + socket.on("pets", function (data) { + var player = players[socket.id]; + var sdata = []; + if (!player) { + return; + } // || is_pvp + for (var id in player.p.pets || {}) { + sdata.push(player.p.pets[id]); + } + socket.emit("players", sdata); + }); + socket.on("harakiri", function (data) { + var player = players[socket.id]; + if (!player || player.rip) { + return; + } + defeat_player(player); + if (!player.rip) { + rip(player); + } + disappearing_text(player.socket, player, "SEPPUKU", { xy: 1, size: "huge", color: "#6F76A6" }); + resend(player, "u+cid"); + }); + socket.on("deepsea", function (data) { + return; + var player = players[socket.id]; + if (!player || player.rip || player.tskin == "deepsea") { + return; + } + player.tskin = "deepsea"; + disappearing_text(player.socket, player, "ROARRRRRRR", { xy: 1, size: "huge", color: "#60A975" }); + resend(player, "u+cid"); + }); + socket.on("blend", function (data) { + var player = players[socket.id]; + var min = 99999; + var x = null; + if (!player || player.rip) { + return; + } + for (var id in instances[player.in].monsters || {}) { + var m = instances[player.in].monsters[id]; + var c = distance(m, player, true); + if (c < min) { + min = c; + x = m; + } + } + if (x) { + var skin = G.monsters[x.type].skin || x.type; + if (player.tskin != skin) { + player.tskin = skin; + resend(player, "u+cid"); + } + } + }); + socket.on("legacify", function (data) { + var player = players[socket.id]; + if (!player || player.rip) { + return; + } + for (var i = 0; i < 42; i++) { + if (0 && player.items[i] && !player.items[i].p && ["fury", "starkillers"].includes(player.items[i].name)) { + player.items[i].p = "legacy"; + } + } + resend(player, "reopen"); + }); + socket.on("requested_ack", function (data) { + // send "requesting_ack", to verify someone is actually connected [03/08/16] + server_log("requested_ack" + JSON.stringify(data), 1); + }); + socket.on("disconnect", function () { + //#IMPORTANT: disconnect exceptions are fatal [07/08/16] + // console.log("disconnect!"); + var player = players[socket.id]; + var observer = observers[socket.id]; + try { + delete sockets[socket.id]; + } catch (e) {} + if (player) { + player.dc = true; + try { + defeat_player(player); + } catch (e) { + log_trace("#X DCERRORPVP", e); + } + // if(!server.live) { server_log('Ignoring the "disconnect" of '+player.name,1); return; } + + try { + if (player.moving && distance(player, { x: player.going_x, y: player.going_y }) < 800) { + player.x = player.going_x; + player.y = player.going_y; + } + } catch (e) { + log_trace("#X DCERRORM", e); + } + // to not get stuck on out-of-bounds corners + + try { + if (player.party) { + leave_party(player.party, player); + } + } catch (e) { + log_trace("#X DCERROR1", e); + } + + try { + xy_emit(player, "disappear", { id: player.id, reason: "disconnect" }); + } catch (e) { + log_trace("#X DCERROR2", e); + } + + server_log("Disconnected Player: " + socket.id + " " + player.name + " Calls: " + socket.total_calls, 1); + + try { + for (var bid in player.bets) { + player.gold += player.bets[bid].gold; + } + player.bets = {}; + } catch (e) { + log_trace("#X DCERRORBETS", e); + } + + try { + if (player.monster) { + remove_monster(player.monster); + } + } catch (e) { + log_trace("#X DCERRORPETS", e); + } + + try { + delete players[socket.id]; + delete instances[player.in].players[player.id]; + if (instances[player.in].solo == player.id) { + destroy_instance(player.in); + } + pmap_remove(player); + } catch (e) { + log_trace("#X DCERROR3 ", e); + } + + try { + restore_state(player, true); + } catch (e) { + log_trace("#X DCERRORrestore", e); + } + + try { + if (gameplay != "hardcore" && gameplay != "test") { + dc_players[player.real_id] = player; + sync_loop(); + } else { + save_player(player); + } + } catch (e) { + log_trace("#X DCERROR", e); + } + } + if (observer) { + server_log("Disconnected Observer: " + socket.id + " Calls: " + socket.total_calls); + try { + delete_observer(socket); + } catch (e) { + log_trace("#X DCERRORO ", e); + } + } + }); + socket.on("shutdown", function (data) { + if (data.pass != variables.access_master) { + return; + } + if (data.reason) { + broadcast("disconnect_reason", data.reason); + } + setTimeout(shutdown_routine, 10000); + }); + socket.on("notice", function (data) { + if (data.pass != variables.access_master) { + return; + } + broadcast("notice", { message: data.message }); + }); + socket.on("render", function (data) { + if (data.pass != variables.access_master) { + return; + } + var player = players[socket.id]; + var output = ""; + var json_output = undefined; + var window = null; + var after = ""; + try { + eval(data.code); + } catch (e) { + output = "Exception: " + e; + } + if (window) { + socket.emit("simple_eval", { + window: window, + after: after, + code: "for(var id in data.window) window[id]=data.window[id]; eval(data.after);", + }); + } else if (json_output !== undefined) { + socket.emit("simple_eval", { output: output, json_output: json_output, code: "show_json(data.json_output)" }); + } else { + socket.emit("simple_eval", { + output: output, + json_output: json_output, + code: 'show_modal("
"+data.output+"
")', + }); + } + resend(player, "reopen+u+cid"); + }); + socket.on("error", function (data) { + // socket.emit("error") brings server down [16/02/22] + }); + socket.on("eval", function (data) { + if (data.command) { + var player = players[socket.id]; + if (!player) { + return; + } + if (player.map != "cyberland" || player.rip) { + return player.socket.emit("game_log", "Not connected to the mainframe"); + } + if (data.command == "hello") { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "hi", + id: "mainframe", + }); + } else if (data.command == "give") { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "what?", + id: "mainframe", + }); + } else if (data.command.startsWith("swap")) { + var numbers = data.command.split(" "); + if (numbers.length != 3) { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "...", + id: "mainframe", + }); + } else { + var a = parseInt(numbers[1]); + var b = parseInt(numbers[2]); + if (0 <= a && a < 42 && 0 <= b && b < 42) { + if (a == player.p.item_num) { + player.p.item_num = b; + } else if (b == player.p.item_num) { + player.p.item_num = a; + } + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "done", + id: "mainframe", + }); + } else { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "ugh, ok", + id: "mainframe", + }); + } + } + } else if (data.command == "stop") { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "mechagnomes assemble", + id: "mainframe", + }); + get_monsters("mechagnome").forEach(function (m) { + if (m.target) { + stop_pursuit(m, { stop: 1, cause: "stop()" }); + } + //else m.irregular=3; + }); + } else if (data.command.startsWith("give") && data.command != "give spares") { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "no", + id: "mainframe", + }); + } else if (data.command == "secret web mode" && (player.p.steam_id || player.p.mas_auth_id)) { + player.p.secret_web_mode = true; + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "secret web mode unlocked", + id: "mainframe", + }); + } else if (data.command == "give spares") { + if (S.misc && S.misc.spares && S.misc.spares.length) { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "here you go", + id: "mainframe", + }); + drop_one_thing(player, S.misc.spares, { x: 1, y: -88 }); + S.misc.spares = []; + } else { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "come later", + id: "mainframe", + }); + } + } else { + if (!player.supercomputer) { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "UNAUTHORIZED COMMAND", + id: "mainframe", + }); + for (var id in instances.cyberland.monsters) { + var monster = instances.cyberland.monsters[id]; + if (!monster.target) { + target_player(monster, player); + } + } + } else { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "UNAUTHORIZED, COMRADE", + id: "mainframe", + }); } } - else - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"UNAUTHORIZED, COMRADE",id:"mainframe"}); } - } - if(data.pass==variables.access_master) - { - eval(data.code); - } + if (data.pass == variables.access_master) { + eval(data.code); + } + }); }); -}); } -function add_pdps(player,target,points) -{ - var pdps_mult=(target&&target.pdps_mult)||(target&&target.difficulty)||1; - player.pdps+=pdps_mult*points; - player.pdps_mult=pdps_mult; +function add_pdps(player, target, points) { + var pdps_mult = (target && target.pdps_mult) || (target && target.difficulty) || 1; + player.pdps += pdps_mult * points; + player.pdps_mult = pdps_mult; // console.log([player.pdps,pdps_mult]); } -function add_coop_points(m,attacker,mnet) -{ - if(!m) return; - if(m["1hp"]) mnet=1; - if(attacker.s.hopsickness) mnet/=4; - m.points[attacker.name]=(m.points[attacker.name]||0)+mnet; - attacker.s.coop={ms:12*60*1000,id:m.id,p:m.points[attacker.name]}; +function add_coop_points(m, attacker, mnet) { + if (!m) { + return; + } + if (m["1hp"]) { + mnet = 1; + } + if (attacker.s.hopsickness) { + mnet /= 4; + } + m.points[attacker.name] = (m.points[attacker.name] || 0) + mnet; + attacker.s.coop = { ms: 12 * 60 * 1000, id: m.id, p: m.points[attacker.name] }; } -function level_monster(monster,args) -{ - if(!args) args={}; - var mult=1; - if(args.delevel) mult=-1; - monster.xp+=G.monsters[monster.type].xp*mult; - monster.max_hp+=parseInt(G.monsters[monster.type].hp/2)*mult; - if(mult>0) monster.hp+=parseInt(G.monsters[monster.type].hp/2)*mult; - else monster.hp=min(monster.hp,monster.max_hp); - monster.level+=1*mult; - monster.u=true; - monster.abs=true; - monster.moving=false; +function level_monster(monster, args) { + if (!args) { + args = {}; + } + var mult = 1; + if (args.delevel) { + mult = -1; + } + monster.xp += G.monsters[monster.type].xp * mult; + monster.max_hp += parseInt(G.monsters[monster.type].hp / 2) * mult; + if (mult > 0) { + monster.hp += parseInt(G.monsters[monster.type].hp / 2) * mult; + } else { + monster.hp = min(monster.hp, monster.max_hp); + } + monster.level += 1 * mult; + monster.u = true; + monster.abs = true; + monster.moving = false; monster.cid++; - monster.last_level=future_s(Math.random()*100-50); - if(mode.fast_mlevels) monster.last_level=new Date(); - if(!args.silent) - { - calculate_monster_stats(monster); - xy_emit(monster,"ui",{id:monster.id,type:"mlevel",mult:mult}); + monster.last_level = future_s(Math.random() * 100 - 50); + if (mode.fast_mlevels) { + monster.last_level = new Date(); } - monster.luckx+=0.25*mult; + if (!args.silent) { + calculate_monster_stats(monster); + xy_emit(monster, "ui", { id: monster.id, type: "mlevel", mult: mult }); + } + monster.luckx += 0.25 * mult; } -function remove_monster(target,args) -{ - if(!args) args={}; - if(!args.method) args.method="death"; - target.dead=true; - target.dc=true; - if(G.monsters[target.type].respawn!=-1 && !G.monsters[target.type].special && !target.special && !target.map_def.special && !target.pet && !target.spawn && !args.nospawn && !target.temp) - { - if(target.map_def.grow && (target.map_def.live||0)<=target.map_def.count*2/3) setTimeout(new_monster_f(target.oin,target.map_def,{before_respawn:target}),25); - else if(G.monsters[target.type].respawn>200) setTimeout(new_monster_f(target.oin,target.map_def,{before_respawn:target}),round(G.monsters[target.type].respawn*(720+Math.random()*480))); - else setTimeout(new_monster_f(target.oin,target.map_def,{before_respawn:target}),round(G.monsters[target.type].respawn*1000+Math.random()*900)); // previously Math.random()*2000 +function remove_monster(target, args) { + if (!args) { + args = {}; } - var luckm=undefined; - if(target.target) - { - var player=players[name_to_id[target.target]]; - if(player) - { - luckm=player.luckm; - if(!args.no_decrease) - reduce_targets(player,target); + if (!args.method) { + args.method = "death"; + } + target.dead = true; + target.dc = true; + if ( + G.monsters[target.type].respawn != -1 && + !G.monsters[target.type].special && + !target.special && + !target.map_def.special && + !target.pet && + !target.spawn && + !args.nospawn && + !target.temp + ) { + if (target.map_def.grow && (target.map_def.live || 0) <= (target.map_def.count * 2) / 3) { + setTimeout(new_monster_f(target.oin, target.map_def, { before_respawn: target }), 25); + } else if (G.monsters[target.type].respawn > 200) { + setTimeout( + new_monster_f(target.oin, target.map_def, { before_respawn: target }), + round(G.monsters[target.type].respawn * (720 + Math.random() * 480)), + ); + } else { + setTimeout( + new_monster_f(target.oin, target.map_def, { before_respawn: target }), + round(G.monsters[target.type].respawn * 1000 + Math.random() * 900), + ); + } // previously Math.random()*2000 + } + var luckm = undefined; + if (target.target) { + var player = players[name_to_id[target.target]]; + if (player) { + luckm = player.luckm; + if (!args.no_decrease) { + reduce_targets(player, target); + } } } - if(!args.silent) xy_emit(target,args.method,{id:target.id,luckm:luckm||1,points:target.cooperative&&target.points||undefined}); + if (!args.silent) { + xy_emit(target, args.method, { + id: target.id, + luckm: luckm || 1, + points: (target.cooperative && target.points) || undefined, + }); + } delete instances[target.in].monsters[target.id]; - if(!args.nospawn) - { + if (!args.nospawn) { target.map_def.live--; monster_c[target.type]--; } } -function new_monster(instance,map_def,args) -{ - if(!instances[instance]) return; //otherwise late dungeon spawns bring down the server - if(!args) args={}; - var monster={},id=++total_monsters+"",name=map_def.type; - monster.id=id; - monster.cid=1; - monster.mult=1; // gets reduced during a server restart - monster.m=0; - monster.outgoing=0; - monster.u=true; - monster.type=name; - if(args.before_respawn && G.monsters[args.before_respawn.type] && G.monsters[args.before_respawn.type].respawn_as) - { - name=monster.type=G.monsters[args.before_respawn.type].respawn_as; - (G.maps[instances[instance].map].monsters||[]).forEach(function(pack){ - if(pack.type==name) - map_def.gold=pack.gold; +function new_monster(instance, map_def, args) { + if (!instances[instance]) { + return; + } //otherwise late dungeon spawns bring down the server + if (!args) { + args = {}; + } + var monster = {}; + var id = ++total_monsters + ""; + var name = map_def.type; + monster.id = id; + monster.cid = 1; + monster.mult = 1; // gets reduced during a server restart + monster.m = 0; + monster.outgoing = 0; + monster.u = true; + monster.type = name; + if (args.before_respawn && G.monsters[args.before_respawn.type] && G.monsters[args.before_respawn.type].respawn_as) { + name = monster.type = G.monsters[args.before_respawn.type].respawn_as; + (G.maps[instances[instance].map].monsters || []).forEach(function (pack) { + if (pack.type == name) { + map_def.gold = pack.gold; + } }); } - var monster_def=G.monsters[monster.type]; - monster.s={}; - monster.a={}; - monster.last={attacked:new Date()}; - monster.level=1; - monster.luckx=1; - monster.xrange=0; - if(map_def.stype=="randomrespawn") - { - var boundary=map_def.boundaries[parseInt(floor(Math.random()*map_def.boundaries.length))]; - if(!server.live) boundary=map_def.boundaries[0]; // to prevent referencing un-created instances - instance=boundary[0]; - map_def.boundary=[boundary[1],boundary[2],boundary[3],boundary[4]]; + var monster_def = G.monsters[monster.type]; + monster.s = {}; + monster.a = {}; + monster.last = { attacked: new Date() }; + monster.level = 1; + monster.luckx = 1; + monster.xrange = 0; + if (map_def.stype == "randomrespawn") { + var boundary = map_def.boundaries[parseInt(floor(Math.random() * map_def.boundaries.length))]; + if (!server.live) { + boundary = map_def.boundaries[0]; + } // to prevent referencing un-created instances + instance = boundary[0]; + map_def.boundary = [boundary[1], boundary[2], boundary[3], boundary[4]]; } - monster.gold=map_def.gold; + monster.gold = map_def.gold; - if(G.dimensions[name]) - { - monster.width=G.dimensions[name][0]; - monster.height=G.dimensions[name][1]; + if (G.dimensions[name]) { + monster.width = G.dimensions[name][0]; + monster.height = G.dimensions[name][1]; } set_base(monster); - if(map_def.random) - { - var spot=random_place(instances[instance].map); - monster.x=spot.x; - monster.y=spot.y; - } - else if(map_def.stype=="pet") - { - monster.pet=true; - monster.x=map_def.x; - monster.y=map_def.y; - monster.owner=map_def.owner; - monster.name=map_def.name; - } - else if(map_def.stype=="trap") - { - monster.trap=true; - monster.x=map_def.x; - monster.y=map_def.y; - monster.owner=map_def.owner; - } - else if(map_def.stype=="spawn") - { - monster.spawn=true; - monster.x=map_def.x; - monster.y=map_def.y; - monster.master=map_def.master; - } - else if(map_def.polygon) - { - var p=random_point(map_def.polygon,monster.base),times=0; - while(!is_xy_safe(instances[instance].map,p[0],p[1])) - { - p=random_point(map_def.polygon,monster.base); - times++; if(times>40) break; + if (map_def.random) { + var spot = random_place(instances[instance].map); + monster.x = spot.x; + monster.y = spot.y; + } else if (map_def.stype == "pet") { + monster.pet = true; + monster.x = map_def.x; + monster.y = map_def.y; + monster.owner = map_def.owner; + monster.name = map_def.name; + } else if (map_def.stype == "trap") { + monster.trap = true; + monster.x = map_def.x; + monster.y = map_def.y; + monster.owner = map_def.owner; + } else if (map_def.stype == "spawn") { + monster.spawn = true; + monster.x = map_def.x; + monster.y = map_def.y; + monster.master = map_def.master; + } else if (map_def.polygon) { + var p = random_point(map_def.polygon, monster.base); + var times = 0; + while (!is_xy_safe(instances[instance].map, p[0], p[1])) { + p = random_point(map_def.polygon, monster.base); + times++; + if (times > 40) { + break; + } } - monster.x=p[0]; monster.y=p[1]; + monster.x = p[0]; + monster.y = p[1]; + } else if (map_def.position) { + monster.x = map_def.position[0] + Math.random() * (2 * map_def.radius) - map_def.radius; + monster.y = map_def.position[1] + Math.random() * (2 * map_def.radius) - map_def.radius; + } else if (map_def.boundary) { + monster.x = map_def.boundary[0] + Math.random() * (map_def.boundary[2] - map_def.boundary[0]); + monster.y = map_def.boundary[1] + Math.random() * (map_def.boundary[3] - map_def.boundary[1]); } - else if(map_def.position) - { - monster.x=map_def.position[0]+Math.random()*(2*map_def.radius)-map_def.radius; - monster.y=map_def.position[1]+Math.random()*(2*map_def.radius)-map_def.radius; - } - else if(map_def.boundary) - { - monster.x=map_def.boundary[0]+Math.random()*(map_def.boundary[2]-map_def.boundary[0]); - monster.y=map_def.boundary[1]+Math.random()*(map_def.boundary[3]-map_def.boundary[1]); - } - ["speed","xp","hp","attack","range","frequency","damage_type","aggro","evasion","avoidance","reflection","armor","resistance","dreturn","rage","apiercing","rpiercing","immune","cooperative","peaceful","drop_on_hit","global","1hp","heal","spawns","lifesteal","manasteal","rbuff","cbuff","projectile","slots","crit","humanoid","explosion","for","difficulty","phresistance"].forEach(function(prop){ - if(prop in monster_def) - monster[prop]=monster_def[prop]; + [ + "speed", + "xp", + "hp", + "attack", + "range", + "frequency", + "damage_type", + "aggro", + "evasion", + "avoidance", + "reflection", + "armor", + "resistance", + "dreturn", + "rage", + "apiercing", + "rpiercing", + "immune", + "cooperative", + "peaceful", + "drop_on_hit", + "global", + "1hp", + "heal", + "spawns", + "lifesteal", + "manasteal", + "rbuff", + "cbuff", + "projectile", + "slots", + "crit", + "humanoid", + "explosion", + "for", + "difficulty", + "phresistance", + ].forEach(function (prop) { + if (prop in monster_def) { + monster[prop] = monster_def[prop]; + } }); - monster.mp=ceil(monster.hp*2/100); - if(monster_def.s) monster.s=clone(monster_def.s); - if(monster_def.abilities) monster.a=monster_def.abilities; - if(monster_def.poisonous) monster.s.poisonous={ms:99999999999999}; - if(mode.range_test) monster.range=2000; - monster.width=monster.height=24; - if(args.temp) - { - monster.temp=1; + monster.mp = ceil((monster.hp * 2) / 100); + if (monster_def.s) { + monster.s = clone(monster_def.s); } - if(map_def.rage) - { - monster.rid=map_def.id; + if (monster_def.abilities) { + monster.a = monster_def.abilities; } - monster.points={}; - for(var a in monster.a) - if(monster.a[a].cooldown) - monster.s[a]={ms:parseInt(monster.a[a].cooldown*Math.random()),ability:true}; - monster.s.young={ms:500}; - monster.is_monster=true; - monster.max_hp=monster_def.hp; - monster.oin=instance; - monster.in=instance; - monster.map=instances[instance].map; - monster.map_def=map_def; - monster.last_level=future_s(Math.random()*100-50); - if(mode.fast_mlevels) monster.last_level=new Date(); - monster.last.attack=new Date(); - monster.last.attacked=really_old; - monster.hits=0; - if(args.last_state) - { - while(monster.levelattacker.max_hp*2.4*mult) - { - lost_xp-=attacker.max_hp*2.4*mult; - lost_xp*=0.9; - if(!attacker.dead) level_monster(attacker); + if (gameplay == "hardcore") { + player.level = max(1, player.level - 1); + player.xp = 0; + } + lost_xp /= 12; + if (player.type == "merchant") { + lost_xp = 0; + } + var mult = 1; + if (attacker["1hp"]) { + mult = 500; + } else if (G.monsters[attacker.type] && G.monsters[attacker.type].special) { + mult = 20; + } + while (lost_xp > attacker.max_hp * 2.4 * mult) { + lost_xp -= attacker.max_hp * 2.4 * mult; + lost_xp *= 0.9; + if (!attacker.dead) { + level_monster(attacker); + } } } -function can_attack(monster,player) -{ - if(is_disabled(monster)) return false; - if(player=="aggro") return mssince(monster.last_aggro)>max(1200,1200/monster.frequency) && mssince(monster.last.attack)>1000/monster.frequency; //aggro check is arbitrary - if(!player) return mssince(monster.last.attack)>1000/monster.frequency; - return distance(player,monster,true)1000/monster.frequency; +function can_attack(monster, player) { + if (is_disabled(monster)) { + return false; + } + if (player == "aggro") { + return ( + mssince(monster.last_aggro) > max(1200, 1200 / monster.frequency) && + mssince(monster.last.attack) > 1000 / monster.frequency + ); + } //aggro check is arbitrary + if (!player) { + return mssince(monster.last.attack) > 1000 / monster.frequency; + } + return distance(player, monster, true) < monster.range && mssince(monster.last.attack) > 1000 / monster.frequency; } -function rage_logic(instance) -{ - if(!instance.rage_list.length || instance.last_rage && mssince(instance.last_rage)<4200) return; - instance.last_rage=new Date(); - instance.rage_list.forEach(function(map_def){ - for(var id in instance.players) - { - var player=instance.players[id]; - if(player && !player.npc && !player.rip && !is_invinc(player) && map_def.rage[0]<=player.x && player.x<=map_def.rage[2] && map_def.rage[1]<=player.y && player.y<=map_def.rage[3]) - { - if(Math.random()380) focus=monster.focus=null,monster.cid++,monster.u=true,change=true; - if(monster.focus) change=true; // better to re-calculate for now, for charge speed changes - for(var name in monster.s) - { - var def=G.conditions[name],ref=monster.s[name],value=monster.s[name].ms; - monster.s[name].ms-=ms; - if(def && def.interval) - { - if(!monster.s[name].last || mssince(monster.s[name].last)>=def.interval) - { - monster.s[name].last=new Date(); - if(name=="eburn") - { - var damage=G.conditions.eburn.damage; - if(monster.immune) damage=0; - disappearing_text({},monster,"-"+damage,{color:"red",xy:1}); - monster.hp=max(1,monster.hp-damage); + var to_push = []; + var monster_map = {}; + var now_date = instance.last_update; + var aggressives = {}; + var targets = {}; + for (var id in instance.monsters) { + var monster = instance.monsters[id]; + var events = []; + var change = false; + var def = monster.type; + if ((monster.target && monster.a.portal) || G.monsters[monster.type].operator) { + instance.operators += 1; + } + var focus = instance.monsters[monster.focus]; + if ((monster.focus && !instance.monsters[monster.focus]) || (focus && distance(monster, focus) > 380)) { + focus = monster.focus = null; + monster.cid++; + monster.u = true; + change = true; + } + if (monster.focus) { + change = true; + } // better to re-calculate for now, for charge speed changes + for (var name in monster.s) { + var def = G.conditions[name]; + var ref = monster.s[name]; + var value = monster.s[name].ms; + monster.s[name].ms -= ms; + if (def && def.interval) { + if (!monster.s[name].last || mssince(monster.s[name].last) >= def.interval) { + monster.s[name].last = new Date(); + if (name == "eburn") { + var damage = G.conditions.eburn.damage; + if (monster.immune) { + damage = 0; + } + disappearing_text({}, monster, "-" + damage, { color: "red", xy: 1 }); + monster.hp = max(1, monster.hp - damage); } - if(name=="eheal") - { - var heal=G.conditions.eheal.heal; - if(monster.immune) heal=0; - disappearing_text({},monster,"+"+heal,{color:"heal",xy:1}); - monster.hp=min(monster.max_hp,monster.hp+heal); + if (name == "eheal") { + var heal = G.conditions.eheal.heal; + if (monster.immune) { + heal = 0; + } + disappearing_text({}, monster, "+" + heal, { color: "heal", xy: 1 }); + monster.hp = min(monster.max_hp, monster.hp + heal); } - if(name=="burned") - { - var damage=ceil(ref.intensity/5); + if (name == "burned") { + var damage = ceil(ref.intensity / 5); //disappearing_text({},monster,"-"+damage,{color:"burn",xy:1}); - monster.hp=max(0,monster.hp-damage); - xy_emit(monster,"hit",{source:"burn",hid:ref.f,id:monster.id,damage:damage,kill:(monster.hp<=0)}); - var attacker=monster.target&&get_player(monster.target)||get_player(ref.f); - var burner=get_player(ref.f); - if(burner) - { - add_pdps(burner,monster,damage); - if(monster.cooperative) add_coop_points(monster,burner,damage); + monster.hp = max(0, monster.hp - damage); + xy_emit(monster, "hit", { + source: "burn", + hid: ref.f, + id: monster.id, + damage: damage, + kill: monster.hp <= 0, + }); + var attacker = (monster.target && get_player(monster.target)) || get_player(ref.f); + var burner = get_player(ref.f); + if (burner) { + add_pdps(burner, monster, damage); + if (monster.cooperative) { + add_coop_points(monster, burner, damage); + } } - if(monster.hp<=0) - { - if(burner) achievement_logic_burn_last_hit(burner); - kill_monster(attacker,monster); + if (monster.hp <= 0) { + if (burner) { + achievement_logic_burn_last_hit(burner); + } + kill_monster(attacker, monster); } } - monster.u=true; + monster.u = true; monster.cid++; } } - if(monster.s[name].ms<=0) - { - if(monster.a[name] && monster.a[name].cooldown) - monster.s[name].ms=monster.a[name].cooldown; - else {delete monster.s[name];} - if(is_disabled(monster) && G.skills[name] && !G.skills[name].passive) continue; - if(name!="young") - { - monster.u=true; monster.cid++; change=true; + if (monster.s[name].ms <= 0) { + if (monster.a[name] && monster.a[name].cooldown) { + monster.s[name].ms = monster.a[name].cooldown; + } else { + delete monster.s[name]; } - if(name=="self_healing") - { - var hp=monster.hp,heal=monster.a.self_healing.heal; - if(monster.s.poisoned) heal/=2; - monster.hp=min(monster.max_hp,monster.hp+heal); - if(hp!=monster.hp) events.push(["ui",{"type":"mheal","id":id,"heal":monster.hp-hp}]); + if (is_disabled(monster) && G.skills[name] && !G.skills[name].passive) { + continue; } - if(name=="healing") - { - var target=monster; - if(focus && distance(focus,monster)<120) target=focus; - var hp=target.hp,heal=monster.a.healing.heal; - if(target.s.poisoned) heal/=2; - target.hp=min(target.max_hp,target.hp+heal); - if(hp!=target.hp) events.push(["ui",{"type":"mheal","id":target.id,"heal":target.hp-hp}]); + if (name != "young") { + monster.u = true; + monster.cid++; + change = true; } - if(name=="mtangle") - { - if(monster.target && get_player(monster.target)) - { - var player=get_player(monster.target); - add_condition(player,"tangled"); - resend(player,"u+cid"); + if (name == "self_healing") { + var hp = monster.hp; + var heal = monster.a.self_healing.heal; + if (monster.s.poisoned) { + heal /= 2; + } + monster.hp = min(monster.max_hp, monster.hp + heal); + if (hp != monster.hp) { + events.push(["ui", { type: "mheal", id: id, heal: monster.hp - hp }]); } } - if(name=="multi_burn") - { - if(monster.cooperative) - for(var name in monster.points||{}) - { - var player=get_player(name); - if(player && simple_distance(monster,player)<480) - { - commence_attack(monster,player,"fireball"); + if (name == "healing") { + var target = monster; + if (focus && distance(focus, monster) < 120) { + target = focus; + } + var hp = target.hp; + var heal = monster.a.healing.heal; + if (target.s.poisoned) { + heal /= 2; + } + target.hp = min(target.max_hp, target.hp + heal); + if (hp != target.hp) { + events.push(["ui", { type: "mheal", id: target.id, heal: target.hp - hp }]); + } + } + if (name == "mtangle") { + if (monster.target && get_player(monster.target)) { + var player = get_player(monster.target); + add_condition(player, "tangled"); + resend(player, "u+cid"); + } + } + if (name == "multi_burn") { + if (monster.cooperative) { + for (var name in monster.points || {}) { + var player = get_player(name); + if (player && simple_distance(monster, player) < 480) { + commence_attack(monster, player, "fireball"); } } - else - for(var id in instances[monster.in].players) - { - var player=instances[monster.in].players[id]; - if(distance(player,monster)<480) - { - commence_attack(monster,player,"fireball"); + } else { + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (distance(player, monster) < 480) { + commence_attack(monster, player, "fireball"); } } - monster.cid++; monster.u=true; change=true; + } + monster.cid++; + monster.u = true; + change = true; } - if(name=="multi_freeze") - { - for(var name in monster.points||{}) - { - var player=get_player(name); - if(player && simple_distance(monster,player)<480) - { - commence_attack(monster,player,"frostball"); + if (name == "multi_freeze") { + for (var name in monster.points || {}) { + var player = get_player(name); + if (player && simple_distance(monster, player) < 480) { + commence_attack(monster, player, "frostball"); } } - monster.cid++; monster.u=true; change=true; + monster.cid++; + monster.u = true; + change = true; } - if(name=="degen") - { - monster.hp-=60; - monster.cid++; monster.u=true; change=true; - if(monster.hp<=0) monster.hp=0,remove_monster(monster); + if (name == "degen") { + monster.hp -= 60; + monster.cid++; + monster.u = true; + change = true; + if (monster.hp <= 0) { + monster.hp = 0; + remove_monster(monster); + } } - if(name=="zap") - { - for(var id in instances[monster.in].players) - { - var player=instances[monster.in].players[id]; - if(distance(player,monster)0.99 || Math.random()interval) - { - var pname=random_one(Object.keys(monster.points)); - var player=get_player(pname); - if(!player || player.npc || distance(monster,player)>400) return; - if(!is_same(player,get_player(monster.target),true)) return; - monster.last[name]=new Date(); - var spot=safe_xy_nearby(player.map,player.x+Math.random()*20-10,player.y+Math.random()*20-10); - if(!spot) return; - new_monster(instance.name,{type:name,stype:"spawn",x:spot.x,y:spot.y,target:player.name,master:monster.id}); + if ( + !monster.pet && + !monster.trap && + mode.aggro && + !monster.target && + monster.aggro && + can_attack(monster, "aggro") + ) { + monster.last_aggro = new Date(); + if (monster.aggro > 0.99 || Math.random() < monster.aggro) { + set_ghash(aggressives, monster, 32); + } + } + if (monster.target && monster.spawns && get_player(monster.target) && !is_disabled(monster)) { + monster.spawns.forEach(function (spi) { + var interval = spi[0]; + var name = spi[1]; + if (!monster.last[name] || mssince(monster.last[name]) > interval) { + var pname = random_one(Object.keys(monster.points)); + var player = get_player(pname); + if (!player || player.npc || distance(monster, player) > 400) { + return; + } + if (!is_same(player, get_player(monster.target), true)) { + return; + } + monster.last[name] = new Date(); + var spot = safe_xy_nearby(player.map, player.x + Math.random() * 20 - 10, player.y + Math.random() * 20 - 10); + if (!spot) { + return; + } + new_monster(instance.name, { + type: name, + stype: "spawn", + x: spot.x, + y: spot.y, + target: player.name, + master: monster.id, + }); } }); } - function attack_target_or_move() - { - var player=players[name_to_id[monster.target]]; - if(player && ssince(monster.last.attacked)>20 && Math.random()>monster.rage*0.99) - { - stop_pursuit(monster,{force:true,cause:"bored"}); + function attack_target_or_move() { + var player = players[name_to_id[monster.target]]; + if (player && ssince(monster.last.attacked) > 20 && Math.random() > monster.rage * 0.99) { + stop_pursuit(monster, { force: true, cause: "bored" }); return; } - if(focus && distance(focus,monster)>40 && !monster.moving) - { - if(mode.all_smart) - { - if(!monster.worker) - { - monster.working=true; - workers[(wlast++)%workers.length].postMessage({type:"fast_astar",in:monster.in,id:monster.id,map:monster.map,sx:monster.x,sy:monster.y,tx:focus.x,ty:focus.y}); + if (focus && distance(focus, monster) > 40 && !monster.moving) { + if (mode.all_smart) { + if (!monster.worker) { + monster.working = true; + workers[wlast++ % workers.length].postMessage({ + type: "fast_astar", + in: monster.in, + id: monster.id, + map: monster.map, + sx: monster.x, + sy: monster.y, + tx: focus.x, + ty: focus.y, + }); } - } - else - { - monster.ogoing_x=monster.going_x,monster.ogoing_y=monster.going_y; - monster.going_x=monster.x+(focus.x-monster.x)/2; - monster.going_y=monster.y+(focus.y-monster.y)/2; - if(mode.path_checks && !can_move(monster)) - { - monster.going_x=monster.ogoing_x,monster.going_y=monster.ogoing_y; - } - else - { + } else { + monster.ogoing_x = monster.going_x; + monster.ogoing_y = monster.going_y; + monster.going_x = monster.x + (focus.x - monster.x) / 2; + monster.going_y = monster.y + (focus.y - monster.y) / 2; + if (mode.path_checks && !can_move(monster)) { + monster.going_x = monster.ogoing_x; + monster.going_y = monster.ogoing_y; + } else { start_moving_element(monster); } } } - if(player && player.in==monster.in && !player.rip && !is_invis(player)) - { - if(distance(player,monster,true)>min(monster.range/1.6,240)+min(100,monster.attack/5.0)+160+(mode.all_smart&&320||1) && !monster.walk_once) stop_pursuit(monster,{cause:"exceeds_range"}); - else if(can_attack(monster,player)) - { - var attack=commence_attack(monster,player,"attack"); - if(attack && attack.events && attack.events.length) events.push(...attack.events); - } - else if(distance(monster,player,true)>12 && !mode.range_test && !(mode.all_smart && monster.moving) && !focus) - { - // console.log(monster.height); - if(mode.all_smart) - { - if(!monster.worker) - { - monster.working=true; - workers[(wlast++)%workers.length].postMessage({type:"fast_astar",in:monster.in,id:monster.id,map:monster.map,sx:monster.x,sy:monster.y,tx:player.x,ty:player.y}); - } + if (player && player.in == monster.in && !player.rip && !is_invis(player)) { + if ( + distance(player, monster, true) > + min(monster.range / 1.6, 240) + min(100, monster.attack / 5.0) + 160 + ((mode.all_smart && 320) || 1) && + !monster.walk_once + ) { + stop_pursuit(monster, { cause: "exceeds_range" }); + } else if (can_attack(monster, player)) { + var attack = commence_attack(monster, player, "attack"); + if (attack && attack.events && attack.events.length) { + events.push(...attack.events); } - else - { - monster.ogoing_x=monster.going_x,monster.ogoing_y=monster.going_y; - monster.going_x=monster.x+(player.x-monster.x)/2; - monster.going_y=monster.y+(player.y-monster.y)/2; - if(mode.path_checks && !can_move(monster)) - { - monster.going_x=monster.ogoing_x,monster.going_y=monster.ogoing_y; - if(monster.attack<120 || distance(monster,player,true)>monster.range) stop_pursuit(monster,{cause:"cant_move"}); + } else if ( + distance(monster, player, true) > 12 && + !mode.range_test && + !(mode.all_smart && monster.moving) && + !focus + ) { + // console.log(monster.height); + if (mode.all_smart) { + if (!monster.worker) { + monster.working = true; + workers[wlast++ % workers.length].postMessage({ + type: "fast_astar", + in: monster.in, + id: monster.id, + map: monster.map, + sx: monster.x, + sy: monster.y, + tx: player.x, + ty: player.y, + }); } - else - { + } else { + monster.ogoing_x = monster.going_x; + monster.ogoing_y = monster.going_y; + monster.going_x = monster.x + (player.x - monster.x) / 2; + monster.going_y = monster.y + (player.y - monster.y) / 2; + if (mode.path_checks && !can_move(monster)) { + monster.going_x = monster.ogoing_x; + monster.going_y = monster.ogoing_y; + if (monster.attack < 120 || distance(monster, player, true) > monster.range) { + stop_pursuit(monster, { cause: "cant_move" }); + } + } else { // console.log("Moving to "+monster.going_x+" "+monster.going_y); start_moving_element(monster); } } } + } else if (monster.target) { + stop_pursuit(monster, { cause: "player_gone" }); + } + if (monster.walk_once) { + monster.walk_once = false; } - else if(monster.target) stop_pursuit(monster,{cause:"player_gone"}); - if(monster.walk_once) monster.walk_once=false; } - if(monster.moving) - { - var ms=mssince(monster.last.move); - ms=min(ms,2000); // to prevent monsters from jumping off the map when the machine sleeps - monster.x+=monster.vx*ms/1000.0; - monster.y+=monster.vy*ms/1000.0; + if (monster.moving) { + var ms = mssince(monster.last.move); + ms = min(ms, 2000); // to prevent monsters from jumping off the map when the machine sleeps + monster.x += (monster.vx * ms) / 1000.0; + monster.y += (monster.vy * ms) / 1000.0; stop_logic(monster); - monster.last.move=new Date(); + monster.last.move = new Date(); xy_u_logic(monster); - if(monster.moving && monster.attack>100 && monster.target) - { + if (monster.moving && monster.attack > 100 && monster.target) { attack_target_or_move(); } - } - else if(monster.s.sleeping || monster.working) - { - - } - else if(can_walk(monster)) // for the .s.stunned check - { - if(monster.s.magiport); - else if(monster.target || focus) - { + } else if (monster.s.sleeping || monster.working) { + } else if (can_walk(monster)) { + // for the .s.stunned check + if (monster.s.magiport) { + } else if (monster.target || focus) { attack_target_or_move(); - } - else if(!mode.upush_test) - { - if(monster.map_def.position && !mode.all_roam) - { - var position=monster.map_def.position,radius=monster.map_def.radius,dx=Math.random()*(radius)-radius/2,dy=Math.random()*(radius)-radius/2; - if(abs(dx)+abs(dy)<80) // optimization to prevent short walks - { - if(dx<0 && dx>-60) dx=-60; if(dx>0 && dx<60) dx=60; - if(dy<0 && dy>-60) dy=-60; if(dy>0 && dy<60) dy=60; + } else if (!mode.upush_test) { + if (monster.map_def.position && !mode.all_roam) { + var position = monster.map_def.position; + var radius = monster.map_def.radius; + var dx = Math.random() * radius - radius / 2; + var dy = Math.random() * radius - radius / 2; + if (abs(dx) + abs(dy) < 80) { + // optimization to prevent short walks + if (dx < 0 && dx > -60) { + dx = -60; + } + if (dx > 0 && dx < 60) { + dx = 60; + } + if (dy < 0 && dy > -60) { + dy = -60; + } + if (dy > 0 && dy < 60) { + dy = 60; + } + } + monster.going_x = monster.x + dx; + monster.going_y = monster.y + dy; + if (monster.going_x > position[0] + radius) { + monster.going_x = position[0] + radius; + } + if (monster.going_y > position[1] + radius) { + monster.going_y = position[1] + radius; + } + if (monster.going_x < position[0] - radius) { + monster.going_x = position[0] - radius; + } + if (monster.going_y < position[1] - radius) { + monster.going_y = position[1] - radius; } - monster.going_x=monster.x+dx; - monster.going_y=monster.y+dy; - if(monster.going_x>position[0]+radius) monster.going_x=position[0]+radius; - if(monster.going_y>position[1]+radius) monster.going_y=position[1]+radius; - if(monster.going_x1 && monster.map_def.polygon) - { - if(is_point_inside([monster.going_x,monster.going_y],monster.map_def.polygon)) break; - else monster.rmove++,monster.dmove++; + } else if ( + monster.map_def.roam || + G.monsters[monster.type].roam || + mode.all_roam || + (monster.map_def.polygon && !monster.irregular) + ) { + perfc.roams += 1; + var map_def = monster.map_def; + var tries = 1; + if (!monster.map_def.roam && !mode.all_roam && monster.map_def.polygon) { + tries = 12; + } + if (!monster.rmove) { + monster.rmove = parseInt(Math.random() * 100); + } + monster.dmove = monster.dmove || 0; + for (var t = 0; t < tries; t++) { + var moves = [ + [1, 0], + [0.8, 0.8], + [0, 1], + [-0.8, 0.8], + [-1, 0], + [-0.8, -0.8], + [0, -1], + [0.8, -0.8], + ]; + var multipliers = [500, 200, 100, 50, 10]; + if (Math.random() < 0.1) { + monster.rmove = parseInt(Math.random() * moves.length); + } + var move = moves[monster.rmove % moves.length]; + var d = multipliers[monster.dmove % multipliers.length]; + monster.going_x = monster.x + move[0] * d; + monster.going_y = monster.y + move[1] * d; + if (tries > 1 && monster.map_def.polygon) { + if (is_point_inside([monster.going_x, monster.going_y], monster.map_def.polygon)) { + break; + } else { + monster.rmove++; + monster.dmove++; + } } } // console.log(monster.rmove+","+monster.dmove+" "+monster.going_x+","+monster.going_y); - if(can_move(monster)) - { + if (can_move(monster)) { start_moving_element(monster); - monster.dmove=0; + monster.dmove = 0; + } else { + monster.rmove++; + monster.dmove++; } - else monster.rmove++,monster.dmove++; - } - else if(monster.map_def.stype!="pet" && monster.map_def.stype!="spawn" && monster.map_def.stype!="trap") - { - var map_def=monster.map_def,to_move=true; - if(map_def.polygon) - { - var p=random_point(map_def.polygon,monster.base); - monster.going_x=p[0]; - monster.going_y=p[1]; + } else if ( + monster.map_def.stype != "pet" && + monster.map_def.stype != "spawn" && + monster.map_def.stype != "trap" + ) { + var map_def = monster.map_def; + var to_move = true; + if (map_def.polygon) { + var p = random_point(map_def.polygon, monster.base); + monster.going_x = p[0]; + monster.going_y = p[1]; + } else { + monster.going_x = map_def.boundary[0] + Math.random() * (map_def.boundary[2] - map_def.boundary[0]); + monster.going_y = map_def.boundary[1] + Math.random() * (map_def.boundary[3] - map_def.boundary[1]); } - else - { - monster.going_x=map_def.boundary[0]+Math.random()*(map_def.boundary[2]-map_def.boundary[0]); - monster.going_y=map_def.boundary[1]+Math.random()*(map_def.boundary[3]-map_def.boundary[1]); - } - if(monster.irregular==3) // new [01/03/19] - { + if (monster.irregular == 3) { + // new [01/03/19] monster.m++; - setTimeout(new_monster_f(monster.oin,monster.map_def,{last_state:monster}),500); - remove_monster(monster,{nospawn:true,method:"disappear"}) - } - else if(monster.irregular==2) - { - server_log("Irregular2 move for "+monster.id); - if(monster.in!=monster.oin) port_monster(monster,{map:monster.oin,x:monster.going_x,y:monster.going_y,in:monster.oin}); - else recalculate_move(monster); - monster.irregular=1; - } - else if(monster.irregular==1) - { - if(!can_move(monster)) - { - monster.m++; - server_log("Irregular1 respawn: "+monster.id); - setTimeout(new_monster_f(monster.oin,monster.map_def,{last_state:monster}),500); - remove_monster(monster,{nospawn:true,method:"disappear"}) + setTimeout(new_monster_f(monster.oin, monster.map_def, { last_state: monster }), 500); + remove_monster(monster, { nospawn: true, method: "disappear" }); + } else if (monster.irregular == 2) { + server_log("Irregular2 move for " + monster.id); + if (monster.in != monster.oin) { + port_monster(monster, { map: monster.oin, x: monster.going_x, y: monster.going_y, in: monster.oin }); + } else { + recalculate_move(monster); } - else - { - server_log("No longer irregular: "+monster.id); + monster.irregular = 1; + } else if (monster.irregular == 1) { + if (!can_move(monster)) { + monster.m++; + server_log("Irregular1 respawn: " + monster.id); + setTimeout(new_monster_f(monster.oin, monster.map_def, { last_state: monster }), 500); + remove_monster(monster, { nospawn: true, method: "disappear" }); + } else { + server_log("No longer irregular: " + monster.id); delete monster["irregular"]; } } - if(monster.going_x!=monster.x || monster.going_y!=monster.y) // so Automatron's don't move + if (monster.going_x != monster.x || monster.going_y != monster.y) { + // so Automatron's don't move start_moving_element(monster); + } } } } - if(!monster.dead && (monster.u || events.length)) - { - monster.u=false; - to_push.push({id:id,entity:monster,data:monster_to_client(monster,events)}); - monster_map[id]=to_push[to_push.length-1]; + if (!monster.dead && (monster.u || events.length)) { + monster.u = false; + to_push.push({ id: id, entity: monster, data: monster_to_client(monster, events) }); + monster_map[id] = to_push[to_push.length - 1]; } } - for(var id in instance.players) - { - var player=instance.players[id]; - if(!player) continue; - for(var name in player.s) - { - var def=G.conditions[name],ref=player.s[name],value=player.s[name].ms; - player.s[name].ms-=ms; - if(def && def.interval) - { - if(!player.s[name].last || mssince(player.s[name].last)>=def.interval) - { - player.s[name].last=new Date(); - if(name=="eburn") - { - disappearing_text(player.socket,player,"-50",{color:"red",xy:1}); - player.hp=max(1,player.hp-50); + for (var id in instance.players) { + var player = instance.players[id]; + if (!player) { + continue; + } + for (var name in player.s) { + var def = G.conditions[name]; + var ref = player.s[name]; + var value = player.s[name].ms; + player.s[name].ms -= ms; + if (def && def.interval) { + if (!player.s[name].last || mssince(player.s[name].last) >= def.interval) { + player.s[name].last = new Date(); + if (name == "eburn") { + disappearing_text(player.socket, player, "-50", { color: "red", xy: 1 }); + player.hp = max(1, player.hp - 50); } - if(name=="eheal") - { - disappearing_text(player.socket,player,"+50",{color:"heal",xy:1}); - player.hp=min(player.max_hp,player.hp+50); + if (name == "eheal") { + disappearing_text(player.socket, player, "+50", { color: "heal", xy: 1 }); + player.hp = min(player.max_hp, player.hp + 50); } - if(name=="burned") - { - var damage=ceil(ref.intensity/5); - disappearing_text(player.socket,player,"-"+damage,{color:"red",xy:1}); - player.hp=max(0,player.hp-damage); - xy_emit(player,"hit",{source:"burn",hid:ref.fid,id:player.name,damage:damage,kill:(player.hp<=0)}); + if (name == "burned") { + var damage = ceil(ref.intensity / 5); + disappearing_text(player.socket, player, "-" + damage, { color: "red", xy: 1 }); + player.hp = max(0, player.hp - damage); + xy_emit(player, "hit", { + source: "burn", + hid: ref.fid, + id: player.name, + damage: damage, + kill: player.hp <= 0, + }); player_rip_logic(player); } - resend(player,"u+cid+nc"); + resend(player, "u+cid+nc"); } } - if(in_arr(name,["damage_received"])) - { - player.s[name].amount=max(0,player.s[name].amount*(4000-ms)/4000.0) + if (in_arr(name, ["damage_received"])) { + player.s[name].amount = max(0, (player.s[name].amount * (4000 - ms)) / 4000.0); } - if(player.s[name].ms<=0) - { + if (player.s[name].ms <= 0) { delete player.s[name]; - if(name=="blink") - { - if(player.s.dampened) - { - xy_emit(player,"ui",{type:"dampened",name:player.name}); - } - else - { - decay_s(player,30000); - transport_player_to(player,ref.in,[ref.x,ref.y,ref.d],"blink"); + if (name == "blink") { + if (player.s.dampened) { + xy_emit(player, "ui", { type: "dampened", name: player.name }); + } else { + decay_s(player, 30000); + transport_player_to(player, ref.in, [ref.x, ref.y, ref.d], "blink"); } } - if(name=="magiport") - { - var dampened=false; - for(var id in instances[ref.in].monsters) - { - var m=instances[ref.in].monsters[id]; - if(m.type=="fieldgen0" && point_distance(ref.x,ref.y,m.x,m.y)<300) - { - xy_emit(player,"ui",{type:"dampened",name:player.name}); - dampened=true; + if (name == "magiport") { + var dampened = false; + for (var id in instances[ref.in].monsters) { + var m = instances[ref.in].monsters[id]; + if (m.type == "fieldgen0" && point_distance(ref.x, ref.y, m.x, m.y) < 300) { + xy_emit(player, "ui", { type: "dampened", name: player.name }); + dampened = true; break; } } - if(!dampened) - { - var skip=false; - if(ref.map!=player.map) skip=true; - decay_s(player,30000); - transport_player_to(player,ref.in,[ref.x,ref.y],"magiport"); - if(skip) - { - resend(player,"u+cid"); + if (!dampened) { + var skip = false; + if (ref.map != player.map) { + skip = true; + } + decay_s(player, 30000); + transport_player_to(player, ref.in, [ref.x, ref.y], "magiport"); + if (skip) { + resend(player, "u+cid"); return; // player isn't in this instance any more } } } - if(def) player.socket.emit("game_response",{response:"ex_condition",name:name}); - if(def && def.ui) resend(player,"u+cid"); // +cid to update the UI's [23/10/18] - else resend(player,"u"); // #TODO: don't need to resend actually, maybe reconsider [27/06/18] - + if (def) { + player.socket.emit("game_response", { response: "ex_condition", name: name }); + } + if (def && def.ui) { + resend(player, "u+cid"); + } // +cid to update the UI's [23/10/18] + else { + resend(player, "u"); + } // #TODO: don't need to resend actually, maybe reconsider [27/06/18] } } - for(var name in player.q) - { - var value=player.q[name].ms,ref=player.q[name]; - player.q[name].ms-=ms; - if(name=="upgrade") - { - if(player.items[ref.num] && player.items[ref.num].name=="placeholder") - { - var def=player.items[ref.num].p,change=false; - if(value1800000 && !player.stealth) - broadcast("server_message",{"message":player.name+" received "+item_to_phrase(item),"color":colors.server_success,item:cache_item(item),"type":"server_csuccess",name:player.name}); - xy_emit(G.maps.main.compound,"upgrade",{type:"compound",success:1}); - if(player.items[ref.num] && player.items[ref.num].name=="placeholder") - { - player.items[ref.num]=item; - player.citems[ref.num]=cache_item(player.items[ref.num]); + if (name == "compound") { + if (player.p.c_item) { + var item = player.p.c_item; + var def = G.items[item.name]; + player.hitchhikers.push([ + "game_response", + { + response: "compound_success", + stale: ref.stale, + level: item.level, + num: data.num, + up: item.extra || undefined, + }, + ]); + if (calculate_item_value(item) + (def.edge || 0) * 2000000 > 1800000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " received " + item_to_phrase(item), + color: colors.server_success, + item: cache_item(item), + type: "server_csuccess", + name: player.name, + }); + } + xy_emit(G.maps.main.compound, "upgrade", { type: "compound", success: 1 }); + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.items[ref.num] = item; + player.citems[ref.num] = cache_item(player.items[ref.num]); + } + achievement_logic_compound_success(player, item); + } else { + var item = player.p.c_itemx; + var def = G.items[item.name]; + player.hitchhikers.push([ + "game_response", + { response: "compound_fail", level: item.level, num: data.num, stale: ref.stale }, + ]); + if (calculate_item_value(item) + (def.edge || 0) * 2000000 > 920000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " lost " + item_to_phrase(item) + "'s", + color: colors.server_failure, + item: cache_item(item), + type: "server_cfail", + name: player.name, + }); + } + xy_emit(G.maps.main.compound, "upgrade", { type: "compound", success: 0 }); + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.citems[ref.num] = player.items[ref.num] = null; + player.esize++; } - achievement_logic_compound_success(player,item); } - else - { - var item=player.p.c_itemx,def=G.items[item.name]; - player.hitchhikers.push(["game_response",{response:"compound_fail",level:item.level,num:data.num,stale:ref.stale}]); - if(calculate_item_value(item)+(def.edge||0)*2000000>920000 && !player.stealth) - broadcast("server_message",{"message":player.name+" lost "+item_to_phrase(item)+"'s","color":colors.server_failure,item:cache_item(item),"type":"server_cfail",name:player.name}); - xy_emit(G.maps.main.compound,"upgrade",{type:"compound",success:0}); - if(player.items[ref.num] && player.items[ref.num].name=="placeholder") - player.citems[ref.num]=player.items[ref.num]=null,player.esize++; - } - delete player.p.c_item; delete player.p.c_itemx; delete player.p.c_roll; - resend(player,"reopen+u+cid+nc+inv"); + delete player.p.c_item; + delete player.p.c_itemx; + delete player.p.c_roll; + resend(player, "reopen+u+cid+nc+inv"); } - if(name=="upgrade") - { - var success=false,announce=false,new_level=player.p.u_level+1; - var item=player.p.u_item||player.p.u_itemx; - var p=player.items[ref.num] && player.items[ref.num].p; - if(player.p.u_item && player.items[ref.num] && player.items[ref.num].name=="placeholder") - { - success=true; - player.items[ref.num]=player.p.u_item; - player.citems[ref.num]=cache_item(player.items[ref.num]); + if (name == "upgrade") { + var success = false; + var announce = false; + var new_level = player.p.u_level + 1; + var item = player.p.u_item || player.p.u_itemx; + var p = player.items[ref.num] && player.items[ref.num].p; + if (player.p.u_item && player.items[ref.num] && player.items[ref.num].name == "placeholder") { + success = true; + player.items[ref.num] = player.p.u_item; + player.citems[ref.num] = cache_item(player.items[ref.num]); + } else if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.citems[ref.num] = player.items[ref.num] = null; + player.esize++; } - else if(player.items[ref.num] && player.items[ref.num].name=="placeholder") - player.citems[ref.num]=player.items[ref.num]=null,player.esize++; - if(player.p.u_type=="offering") - { - if(success) player.hitchhikers.push(["game_response",{response:"upgrade_offering_success",stale:ref.stale}]); + if (player.p.u_type == "offering") { + if (success) { + player.hitchhikers.push(["game_response", { response: "upgrade_offering_success", stale: ref.stale }]); + } + } else if (player.p.u_type == "stat") { + if (success) { + player.hitchhikers.push([ + "game_response", + { + response: "upgrade_success_stat", + stale: ref.stale, + stat_type: p && G.items[p.scroll].stat, + num: ref.num, + }, + ]); + } + } else if (!ref.silent) { + announce = true; } - else if(player.p.u_type=="stat") - { - if(success) player.hitchhikers.push(["game_response",{response:"upgrade_success_stat",stale:ref.stale,stat_type:p&&G.items[p.scroll].stat,num:ref.num}]); + + if (success && !player.p.u_fail) { + if (announce) { + player.hitchhikers.push([ + "game_response", + { response: "upgrade_success", level: new_level, num: ref.num, stale: ref.stale }, + ]); + } + if (announce && calculate_item_value(item) > 4800000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " received " + item_to_phrase(item), + color: colors.server_success, + item: cache_item(item), + type: "server_usuccess", + name: player.name, + }); + } + xy_emit((instances.main && G.maps.main.upgrade) || player, "upgrade", { type: "upgrade", success: 1 }); + achievement_logic_upgrade_success(player, item); + } else { + player.hitchhikers.push([ + "game_response", + { response: "upgrade_fail", level: new_level, num: ref.num, stale: ref.stale }, + ]); + if (announce && calculate_item_value(item) > 4800000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " lost " + item_to_phrase(item), + color: colors.server_failure, + item: cache_item(item), + type: "server_ufail", + name: player.name, + }); + } + xy_emit((instances.main && G.maps.main.upgrade) || player, "upgrade", { type: "upgrade", success: 0 }); } - else if(!ref.silent) - announce=true; - - if(success && !player.p.u_fail) - { - if(announce) player.hitchhikers.push(["game_response",{response:"upgrade_success",level:new_level,num:ref.num,stale:ref.stale}]); - if(announce && calculate_item_value(item)>4800000 && !player.stealth) - broadcast("server_message",{"message":player.name+" received "+item_to_phrase(item),"color":colors.server_success,item:cache_item(item),"type":"server_usuccess",name:player.name}); - xy_emit(instances.main&&G.maps.main.upgrade||player,"upgrade",{type:"upgrade",success:1}); - achievement_logic_upgrade_success(player,item); - } - else - { - player.hitchhikers.push(["game_response",{response:"upgrade_fail",level:new_level,num:ref.num,stale:ref.stale}]); - if(announce && calculate_item_value(item)>4800000 && !player.stealth) - broadcast("server_message",{"message":player.name+" lost "+item_to_phrase(item),"color":colors.server_failure,item:cache_item(item),"type":"server_ufail",name:player.name}); - xy_emit(instances.main&&G.maps.main.upgrade||player,"upgrade",{type:"upgrade",success:0}); - } - delete player.p.u_item; delete player.p.u_type; delete player.p.u_itemx; delete player.p.u_roll; delete player.p.u_fail; delete player.p.u_level; - resend(player,"reopen+u+cid+nc+inv"); + delete player.p.u_item; + delete player.p.u_type; + delete player.p.u_itemx; + delete player.p.u_roll; + delete player.p.u_fail; + delete player.p.u_level; + resend(player, "reopen+u+cid+nc+inv"); } - if(name=="slots") - { - if(Math.random()<((S.gold>500000000)&&D.odds.slots_good||D.odds.slots)) - { - var gold=500000000; - player.gold+=gold; S.gold-=gold; - broadcast("server_message",{"message":player.name+" received "+to_pretty_num(gold)+" gold","color":"gold"}); - player.socket.emit("game_response","slots_success"); - player.socket.emit("game_log",{message:"Received "+to_pretty_num(gold)+" gold",color:"gold"}); + if (name == "slots") { + if (Math.random() < ((S.gold > 500000000 && D.odds.slots_good) || D.odds.slots)) { + var gold = 500000000; + player.gold += gold; + S.gold -= gold; + broadcast("server_message", { + message: player.name + " received " + to_pretty_num(gold) + " gold", + color: "gold", + }); + player.socket.emit("game_response", "slots_success"); + player.socket.emit("game_log", { message: "Received " + to_pretty_num(gold) + " gold", color: "gold" }); // resend(player,"u+cid"); + } else { + player.socket.emit("game_response", "slots_fail"); } - else player.socket.emit("game_response","slots_fail"); } } } - for(var name in player.c) - { - player.c[name].ms-=ms; - if(player.c[name].ms<=0) - { - var ref=player.c[name]; + for (var name in player.c) { + player.c[name].ms -= ms; + if (player.c[name].ms <= 0) { + var ref = player.c[name]; delete player.c[name]; - if(name=="town") - { - decay_s(player,4000); - player.last.town=new Date(); - transport_player_to(player,player.in,undefined,1); + if (name == "town") { + decay_s(player, 4000); + player.last.town = new Date(); + transport_player_to(player, player.in, undefined, 1); resend(player); } - if(name=="pickpocket") - { - var target=get_player(ref.target); - if(!target) player.socket.emit("game_response",{response:"pick_failed",cevent:true,reason:"player_gone"}); - else if(distance(player,target)>20) player.socket.emit("game_response",{response:"pick_failed",cevent:true,reason:"distance"}); - else - { - var num=floor(Math.random()*42); - if(target.items[num] && target.items[num].v) - { - var item=target.items[num]; - if(item.q && item.q>1) - { - target.items[num].q-=1; target.citems[num].q-=1; - item=create_new_item(item.name); + if (name == "pickpocket") { + var target = get_player(ref.target); + if (!target) { + player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "player_gone" }); + } else if (distance(player, target) > 20) { + player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "distance" }); + } else { + var num = floor(Math.random() * 42); + if (target.items[num] && target.items[num].v) { + var item = target.items[num]; + if (item.q && item.q > 1) { + target.items[num].q -= 1; + target.citems[num].q -= 1; + item = create_new_item(item.name); + } else { + target.citems[num] = target.items[num] = null; } - else - { - target.citems[num]=target.items[num]=null; - } - item.v=new Date(); - add_item(player,item); - player.socket.emit("game_response",{response:"picked",slot:"mainhand",cevent:true}); - consume_skill(player,"pickpocket",true); - resend(player,"reopen"); resend(target,"reopen"); - target.socket.emit("game_response",{response:"got_picked",cevent:true}); - } - else - { - player.socket.emit("game_response",{response:"pick_failed",cevent:true,reason:"misfortune"}); + item.v = new Date(); + add_item(player, item); + player.socket.emit("game_response", { response: "picked", slot: "mainhand", cevent: true }); + consume_skill(player, "pickpocket", true); + resend(player, "reopen"); + resend(target, "reopen"); + target.socket.emit("game_response", { response: "got_picked", cevent: true }); + } else { + player.socket.emit("game_response", { response: "pick_failed", cevent: true, reason: "misfortune" }); } } } - if(name=="fishing") - { - if(Math.random()<0.1) - { - if(player.esize) exchange(player,ref.drop,{phrase:"Fished"}); - consume_skill(player,"fishing",true); - if(player.slots.mainhand) - { - var prop=calculate_item_properties(player.slots.mainhand); - if(prop.breaks && Math.random()player.slots.elixir.expires) - { - try{ - var def=G.items[player.slots.elixir.name]; - player.socket.emit("game_log",def.name+" wore off ..."); - if(def.withdrawal) add_condition(player,def.withdrawal); - }catch(e){} - player.slots.elixir=null; - player.cslots.elixir=null; - resend(player,"reopen+u+cid"); + if (player.slots.elixir && player.slots.elixir.expires && now_date > player.slots.elixir.expires) { + try { + var def = G.items[player.slots.elixir.name]; + player.socket.emit("game_log", def.name + " wore off ..."); + if (def.withdrawal) { + add_condition(player, def.withdrawal); + } + } catch (e) {} + player.slots.elixir = null; + player.cslots.elixir = null; + resend(player, "reopen+u+cid"); } - if(player.moving) - { - var ms=mssince(player.last.move); - player.x+=player.vx*ms/1000.0; - player.y+=player.vy*ms/1000.0; - player.red_zone*=0.99; - if(smap_data[player.map]!=-1 && !player.npc && mode.red_zone && !player.s.dash) - { - var current=smap_data[player.map][phash(player)]; - if(current===undefined) current=8; - current=max(0,current-1); // 1 is the new 0 [01/08/18] - player.red_zone+=current; + if (player.moving) { + var ms = mssince(player.last.move); + player.x += (player.vx * ms) / 1000.0; + player.y += (player.vy * ms) / 1000.0; + player.red_zone *= 0.99; + if (smap_data[player.map] != -1 && !player.npc && mode.red_zone && !player.s.dash) { + var current = smap_data[player.map][phash(player)]; + if (current === undefined) { + current = 8; + } + current = max(0, current - 1); // 1 is the new 0 [01/08/18] + player.red_zone += current; // console.log(player.red_zone); - if(player.red_zone>smap_edge) - { - player.red_zone=0; - player.hp-=parseInt(player.hp/2+1000); - player.socket.emit("game_log","Received a movement penalty"); - player.socket.emit("game_log","This might have happened if your network is too slow"); - appengine_log("violation","red_zone: "+player.name+" afk: "+player.afk+" code: "+player.code); - transport_player_to(player,player.map); + if (player.red_zone > smap_edge) { + player.red_zone = 0; + player.hp -= parseInt(player.hp / 2 + 1000); + player.socket.emit("game_log", "Received a movement penalty"); + player.socket.emit("game_log", "This might have happened if your network is too slow"); + appengine_log("violation", "red_zone: " + player.name + " afk: " + player.afk + " code: " + player.code); + transport_player_to(player, player.map); defeat_player(player); - if(player.hp<=0) rip(player); - resend(player,"u+cid"); + if (player.hp <= 0) { + rip(player); + } + resend(player, "u+cid"); } } stop_logic(player); // if(!player.moving) player.check_x=player.x,player.check_y=player.y,player.checked_xy=false; - player.last.move=new Date(); + player.last.move = new Date(); xy_u_logic(player); xy_upush_logic(player); - if(!player.npc) pmap_move(player); + if (!player.npc) { + pmap_move(player); + } + } else { + player.red_zone *= 0.97; } - else - { - player.red_zone*=0.97; - } - if(mode.aggro && !player.rip && !is_invinc(player) && !player.npc) - { + if (mode.aggro && !player.rip && !is_invinc(player) && !player.npc) { //#GTODO: calculate an angle + compare against monster's .angle before attacking - var l=get_nearby_ghash(aggressives,player,32); - l.forEach(function(monster){ - if(is_in_front(monster,player) && can_attack(monster,player)) - { - if(Math.random()=3) player.socket.disconnect(); + if (player.u) { + player.u = false; + to_push.push({ id: id, entity: player, data: player_to_client(player, 1) }); + } + if (player.violations >= 3) { + player.socket.disconnect(); + } } - (G.maps[instance.map].traps||[]).forEach(function(trap){ - if(trap.type=="spikes") - for(var id in pmap_get({x:trap.position[0],y:trap.position[1],in:instance.name})) - { - var player=players[id_to_id[id]]; - if(player && !player.npc) - { - disappearing_text(player.socket,player,"-50",{color:"red",xy:1}); - player.hp=max(0,player.hp-50); + (G.maps[instance.map].traps || []).forEach(function (trap) { + if (trap.type == "spikes") { + for (var id in pmap_get({ x: trap.position[0], y: trap.position[1], in: instance.name })) { + var player = players[id_to_id[id]]; + if (player && !player.npc) { + disappearing_text(player.socket, player, "-50", { color: "red", xy: 1 }); + player.hp = max(0, player.hp - 50); player_rip_logic(player); - resend(player,"u+cid"); + resend(player, "u+cid"); } } - else if(trap.type=="debuff") - for(var id in instance.players) - { - var player=instance.players[id]; - if(is_point_inside([player.x,player.y],trap.polygon)) - { + } else if (trap.type == "debuff") { + for (var id in instance.players) { + var player = instance.players[id]; + if (is_point_inside([player.x, player.y], trap.polygon)) { // player.s["debuffaura"]={"ms":200,"name":"Debuff","skin":"citizens","citizens":true,"speed":-40}; - add_condition(player,"slowness",{duration:200}); - resend(player,"u+cid"); + add_condition(player, "slowness", { duration: 200 }); + resend(player, "u+cid"); } } - + } }); - for(var id in instance.players) send_xy_updates(instance.players[id],to_push); - for(var id in instance.observers) send_xy_updates(instance.observers[id],to_push); + for (var id in instance.players) { + send_xy_updates(instance.players[id], to_push); + } + for (var id in instance.observers) { + send_xy_updates(instance.observers[id], to_push); + } } -function count_unique_users() -{ - unique_players=0; - var marked={}; - for(var id in players) - { - var ip=get_ip(players[id]); - if(!marked[ip]) {marked[ip]=1; unique_players++;} - } - if(is_sdk) unique_players=9; - for(name in instances) - { - var instance=instances[name]; - if(!instance.pvp) continue; - var initial=instance.allow,npc=npcs.pvp; - if(is_sdk && Object.keys(players).length>=2 || unique_players>=B.arena_limit || Object.keys(instance.players).length) // direction logic at game.js/update_sprite - { - if(instance.allow) continue; - instance.allow=true; - if(!npc) continue; - npc.going_x=npc.positions[1][0]; - npc.going_y=npc.positions[1][1]; - npc.allow=true; - npc.u=true; - start_moving_element(npc); +function count_unique_users() { + unique_players = 0; + var marked = {}; + for (var id in players) { + var ip = get_ip(players[id]); + if (!marked[ip]) { + marked[ip] = 1; + unique_players++; } - else - { - if(!instance.allow) continue; - instance.allow=false; - if(!npc) continue; - npc.going_x=npc.positions[0][0]; - npc.going_y=npc.positions[0][1]; - npc.allow=false; - npc.u=true; + } + if (is_sdk) { + unique_players = 9; + } + for (name in instances) { + var instance = instances[name]; + if (!instance.pvp) { + continue; + } + var initial = instance.allow; + var npc = npcs.pvp; + if ( + (is_sdk && Object.keys(players).length >= 2) || + unique_players >= B.arena_limit || + Object.keys(instance.players).length + ) { + // direction logic at game.js/update_sprite + if (instance.allow) { + continue; + } + instance.allow = true; + if (!npc) { + continue; + } + npc.going_x = npc.positions[1][0]; + npc.going_y = npc.positions[1][1]; + npc.allow = true; + npc.u = true; + start_moving_element(npc); + } else { + if (!instance.allow) { + continue; + } + instance.allow = false; + if (!npc) { + continue; + } + npc.going_x = npc.positions[0][0]; + npc.going_y = npc.positions[0][1]; + npc.allow = false; + npc.u = true; start_moving_element(npc); } } } -var last_iloop=new Date(); -function instance_loop() -{ - var ms_since=32; - try{ - var now_date=new Date(); +var last_iloop = new Date(); +function instance_loop() { + var ms_since = 32; + try { + var now_date = new Date(); - for(name in instances) - { - var instance=instances[name]; - if(mssince(instance.last_update)>75) update_instance(instance); + for (name in instances) { + var instance = instances[name]; + if (mssince(instance.last_update) > 75) { + update_instance(instance); + } } - ms_since=mssince(now_date); - if(ms_since>B.instance_loop_log_edge) server_log("Instance loop took "+mssince(now_date)+"ms",1); - perfc.instance_loop[ms_since]=(perfc.instance_loop[ms_since]||0)+1; - perfc.instance_loops+=1; - var mss_since=mssince(last_iloop); - perfc.instance_delay[mss_since]=(perfc.instance_delay[mss_since]||0)+1; - if(mss_since/1000>15) - console.log("Sleep detected: "+(mss_since/1000)); - last_iloop=new Date(); - }catch(e){log_trace("#X Instance loop error",e);}; - setTimeout(instance_loop,max(75,min(1000,ms_since*2+2))); + ms_since = mssince(now_date); + if (ms_since > B.instance_loop_log_edge) { + server_log("Instance loop took " + mssince(now_date) + "ms", 1); + } + perfc.instance_loop[ms_since] = (perfc.instance_loop[ms_since] || 0) + 1; + perfc.instance_loops += 1; + var mss_since = mssince(last_iloop); + perfc.instance_delay[mss_since] = (perfc.instance_delay[mss_since] || 0) + 1; + if (mss_since / 1000 > 15) { + console.log("Sleep detected: " + mss_since / 1000); + } + last_iloop = new Date(); + } catch (e) { + log_trace("#X Instance loop error", e); + } + setTimeout(instance_loop, max(75, min(1000, ms_since * 2 + 2))); } -function npc_loop() // now mainly just NPC's, back in the day pretty much everything [11/08/22] -{ - if(!server.live) return setTimeout(npc_loop,10); - var ms_since=32; - try{ - var now_date=new Date(); +function npc_loop() { + // now mainly just NPC's, back in the day pretty much everything [11/08/22] + if (!server.live) { + return setTimeout(npc_loop, 10); + } + var ms_since = 32; + try { + var now_date = new Date(); - for(var id in npcs) - { - var npc=npcs[id],delay=-npc.delay*npc.d_multiplier; - if(npc.rip) continue; - var def=G.npcs[npc.ntype]||{}; - if(def && instances[npc.in] && instances[npc.in].players && def.aura && (!npc.last_aura || mssince(npc.last_aura)>2000)) - { - npc.last_aura=new Date(); - for(var pid in instances[npc.in].players) - { - var player=instances[npc.in].players[pid]; + for (var id in npcs) { + var npc = npcs[id]; + var delay = -npc.delay * npc.d_multiplier; + if (npc.rip) { + continue; + } + var def = G.npcs[npc.ntype] || {}; + if ( + def && + instances[npc.in] && + instances[npc.in].players && + def.aura && + (!npc.last_aura || mssince(npc.last_aura) > 2000) + ) { + npc.last_aura = new Date(); + for (var pid in instances[npc.in].players) { + var player = instances[npc.in].players[pid]; // console.log(player); - if(!player.npc && distance(player,npc)<320) - { - player.s[npc.ntype+"aura"]={"ms":6000,"name":"Citizen's Aura","skin":"citizens","citizens":true}; - for(var p in def.aura) player.s[npc.ntype+"aura"][p]=def.aura[p]; - resend(player,"u+cid"); + if (!player.npc && distance(player, npc) < 320) { + player.s[npc.ntype + "aura"] = { ms: 6000, name: "Citizen's Aura", skin: "citizens", citizens: true }; + for (var p in def.aura) { + player.s[npc.ntype + "aura"][p] = def.aura[p]; + } + resend(player, "u+cid"); } } - } - if(def.attack && ssince(npc.last.attack)>1.5) - { - for(var id in instances[npc.in].monsters) - { - var monster=instances[npc.in].monsters[id]; - if(distance(npc,monster)5000 - { - npc.last.attack=new Date(); - commence_attack(npc,monster,"attack"); + if (def.attack && ssince(npc.last.attack) > 1.5) { + for (var id in instances[npc.in].monsters) { + var monster = instances[npc.in].monsters[id]; + if (distance(npc, monster) < npc.range) { + // && monster.hp>5000 + npc.last.attack = new Date(); + commence_attack(npc, monster, "attack"); break; } } } - if(def.seek) - { - var target=npc.focus && get_player(npc.focus); - if(target && def.transport && target.map!=npc.map && target.in==target.map) - { - var spot=safe_xy_nearby(target.map,target.x-8,target.y-6); - if(spot) - transport_player_to(npc,get_player(npc.focus).map,[spot.x,spot.y]); + if (def.seek) { + var target = npc.focus && get_player(npc.focus); + if (target && def.transport && target.map != npc.map && target.in == target.map) { + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (spot) { + transport_player_to(npc, get_player(npc.focus).map, [spot.x, spot.y]); + } } - var target=null,old_focus=npc.focus,min_val=99999,max_val=-99999; + var target = null; + var old_focus = npc.focus; + var min_val = 99999; + var max_val = -99999; - for(var pid in instances[npc.in].players) - { - var player=instances[npc.in].players[pid]; - if(def.seek=="low_hp" && player.hpmax_val) - max_val=player.cuteness,target=player; - if(def.seek=="thrill" && player.thrilling && player.thrilling>now_date && (!target || player.thrilling>target.thrilling)) - target=player; - if(def.seek=="gold" && player.gold>1000000000*(!is_pvp&&10||1) && player.gold>max_val) - max_val=player.gold,target=player; - if(player.type=="merchant") continue; - if(def.seek=="dragondagger" && player.slots.mainhand && player.slots.mainhand.name=="dragondagger" && player.level max_val) { + max_val = player.cuteness; + target = player; + } + if ( + def.seek == "thrill" && + player.thrilling && + player.thrilling > now_date && + (!target || player.thrilling > target.thrilling) + ) { + target = player; + } + if (def.seek == "gold" && player.gold > 1000000000 * ((!is_pvp && 10) || 1) && player.gold > max_val) { + max_val = player.gold; + target = player; + } + if (player.type == "merchant") { + continue; + } + if ( + def.seek == "dragondagger" && + player.slots.mainhand && + player.slots.mainhand.name == "dragondagger" && + player.level < min_val + ) { + min_val = player.level; + target = player; + } } - npc.focus=target&&target.name; - if(old_focus!=npc.focus) - npc.u=true,npc.cid++; + npc.focus = target && target.name; + if (old_focus != npc.focus) { + npc.u = true; + npc.cid++; + } } - if(!npc.movable) continue; - var moves=[[1,0],[0,1],[-1,0],[0,-1],[0.8,0.8],[-0.8,-0.8],[0.8,-0.8],[-0.8,0.8]],multiplier=npc.steps; shuffle(moves); - if(def.aura && G.maps[npc.map].ref.transporter && distance(npc,G.maps[npc.map].ref.transporter)<2000) delay=-2400,multiplier*=4; - else if(def.aura) delay*=3; - if(!npc.citizen || npc.moving || npc.last.move>future_ms(delay) || instances[npc.in].paused) continue; - if(Math.random()<0.3) multiplier*=2; - else if(Math.random()<0.3) multiplier*=4; + if (!npc.movable) { + continue; + } + var moves = [ + [1, 0], + [0, 1], + [-1, 0], + [0, -1], + [0.8, 0.8], + [-0.8, -0.8], + [0.8, -0.8], + [-0.8, 0.8], + ]; + var multiplier = npc.steps; + shuffle(moves); + if (def.aura && G.maps[npc.map].ref.transporter && distance(npc, G.maps[npc.map].ref.transporter) < 2000) { + delay = -2400; + multiplier *= 4; + } else if (def.aura) { + delay *= 3; + } + if (!npc.citizen || npc.moving || npc.last.move > future_ms(delay) || instances[npc.in].paused) { + continue; + } + if (Math.random() < 0.3) { + multiplier *= 2; + } else if (Math.random() < 0.3) { + multiplier *= 4; + } - if(def.heal) - { - for(var id in instances[npc.in].players) - { - var player=instances[npc.in].players[id]; - if(distance(player,npc)<320 && player.hpnpc.boundary[2] || npc.going_ynpc.boundary[3])); - else if(can_move(npc)) - { - npc.u=true; + if ( + npc.boundary && + (npc.going_x < npc.boundary[0] || + npc.going_x > npc.boundary[2] || + npc.going_y < npc.boundary[1] || + npc.going_y > npc.boundary[3]) + ) { + } else if (can_move(npc)) { + npc.u = true; start_moving_element(npc); // server_log("Moving to "+npc.going_x+","+npc.going_y); - } - else - { - npc.going_x=npc.x; npc.going_y=npc.y; - npc.last.move=new Date(); + } else { + npc.going_x = npc.x; + npc.going_y = npc.y; + npc.last.move = new Date(); } } - ms_since=mssince(now_date); - }catch(e){log_trace("#X NPC loop error",e);}; - setTimeout(npc_loop,max(28,min(1000,ms_since*2+2))); // originally 24 + ms_since = mssince(now_date); + } catch (e) { + log_trace("#X NPC loop error", e); + } + setTimeout(npc_loop, max(28, min(1000, ms_since * 2 + 2))); // originally 24 } -function game_loop() // back in the day pretty much everything was in here [11/08/22] -{ - if(!server.live) return setTimeout(game_loop,10); - var ms_since=32; - try{ - var now_date=new Date(); +function game_loop() { + // back in the day pretty much everything was in here [11/08/22] + if (!server.live) { + return setTimeout(game_loop, 10); + } + var ms_since = 32; + try { + var now_date = new Date(); // for(name in instances) // { // var instance=instances[name]; // if(mssince(instance.last_update)>75) update_instance(instance); // } - for(var id in server.s) - { - server.s[id].ms-=10; - if(server.s[id].ms<0) delete server.s[id]; + for (var id in server.s) { + server.s[id].ms -= 10; + if (server.s[id].ms < 0) { + delete server.s[id]; + } } - ms_since=mssince(now_date); - if(ms_since>B.game_loop_log_edge) server_log("Main loop took "+mssince(now_date)+"ms",1); - perfc.game_loop[ms_since]=(perfc.game_loop[ms_since]||0)+1; - perfc.game_loops+=1; - }catch(e){log_trace("#X Main loop error",e);}; - setTimeout(game_loop,max(28,min(1000,ms_since*2+2))); // originally 24 + ms_since = mssince(now_date); + if (ms_since > B.game_loop_log_edge) { + server_log("Main loop took " + mssince(now_date) + "ms", 1); + } + perfc.game_loop[ms_since] = (perfc.game_loop[ms_since] || 0) + 1; + perfc.game_loops += 1; + } catch (e) { + log_trace("#X Main loop error", e); + } + setTimeout(game_loop, max(28, min(1000, ms_since * 2 + 2))); // originally 24 } -var lrid=0; -function aura_loop() -{ - try{ +var lrid = 0; +function aura_loop() { + try { lrid++; - for(var id in players) - { - var player=players[id]; - if(!player || !player.aura || (player.rid%5)!=(lrid%5)) continue; - for(var aid in player.aura) - { - for(var pid in instances[player.in].players) - { - if(!is_same(instances[player.in].players[pid],player,3)) continue; - if(distance(instances[player.in].players[pid],player)>200) continue; - instances[player.in].players[pid].s[aid]={ms:G.conditions[aid].duration||30000,f:player.name}; - if(G.conditions[aid].attr0) - instances[player.in].players[pid].s[aid][G.conditions[aid].attr0]=player.aura[aid].attr0; - resend(instances[player.in].players[pid],"u+cid"); + for (var id in players) { + var player = players[id]; + if (!player || !player.aura || player.rid % 5 != lrid % 5) { + continue; + } + for (var aid in player.aura) { + for (var pid in instances[player.in].players) { + if (!is_same(instances[player.in].players[pid], player, 3)) { + continue; + } + if (distance(instances[player.in].players[pid], player) > 200) { + continue; + } + instances[player.in].players[pid].s[aid] = { ms: G.conditions[aid].duration || 30000, f: player.name }; + if (G.conditions[aid].attr0) { + instances[player.in].players[pid].s[aid][G.conditions[aid].attr0] = player.aura[aid].attr0; + } + resend(instances[player.in].players[pid], "u+cid"); } } } - }catch(e){log_trace("#X aura loop error",e);}; - setTimeout(aura_loop,1000); + } catch (e) { + log_trace("#X aura loop error", e); + } + setTimeout(aura_loop, 1000); } -setTimeout(game_loop,10); -setTimeout(npc_loop,10); -setTimeout(instance_loop,10); -setTimeout(aura_loop,10); -setInterval(count_unique_users,6000); +setTimeout(game_loop, 10); +setTimeout(npc_loop, 10); +setTimeout(instance_loop, 10); +setTimeout(aura_loop, 10); +setInterval(count_unique_users, 6000); -setInterval(function(){ - try{ - for(var id in observers) - { - var observer=observers[id]; - if(observer.player && get_player(observer.player.name)) - { - var player=get_player(observer.player.name); - if(simple_distance(observer,player)>200) - transport_observer_to(observer,player.in,player.map,player.x,player.y+(observer.socket.desktop&&120||0)); - } - else - { - if(simple_distance(observer,{map:observer_map,in:observer_map,x:observer_x,y:observer_y})>200) - transport_observer_to(observer,observer_map,observer_map,observer_x,observer_y+(observer.socket.desktop&&120||0)); +setInterval(function () { + try { + for (var id in observers) { + var observer = observers[id]; + if (observer.player && get_player(observer.player.name)) { + var player = get_player(observer.player.name); + if (simple_distance(observer, player) > 200) { + transport_observer_to( + observer, + player.in, + player.map, + player.x, + player.y + ((observer.socket.desktop && 120) || 0), + ); + } + } else { + if (simple_distance(observer, { map: observer_map, in: observer_map, x: observer_x, y: observer_y }) > 200) { + transport_observer_to( + observer, + observer_map, + observer_map, + observer_x, + observer_y + ((observer.socket.desktop && 120) || 0), + ); + } } } - }catch(e){log_trace("#X observer loop2 error",e);}; -},13200); + } catch (e) { + log_trace("#X observer loop2 error", e); + } +}, 13200); -setInterval(function(){ - try{ - if(is_pvp) return; - var names=[],player; - total_merchants=0; - for(var id in players) - { - player=players[id]; - if(!player.npc && player.last.attack && player.map==player.in && ssince(player.last.attack)<3 && !G.maps[player.map].pvp) - { +setInterval(function () { + try { + if (is_pvp) { + return; + } + var names = []; + var player; + total_merchants = 0; + for (var id in players) { + player = players[id]; + if ( + !player.npc && + player.last.attack && + player.map == player.in && + ssince(player.last.attack) < 3 && + !G.maps[player.map].pvp + ) { names.push(player.name); } - if(player.p && player.p.stand) total_merchants+=1; - } - if(names.length) - { - player=get_player(names[floor(Math.random()*names.length)]); - observer_map=player.map; - observer_x=parseInt(player.x); - observer_y=parseInt(player.y); - server_log("Set the observer location: "+[observer_map,observer_x,observer_y]); - } - else - { - observer_map=merchant_map; - observer_x=merchant_x; - observer_y=merchant_y; - } - - }catch(e){log_trace("#X observer loop error",e);}; -},4000); - -setTimeout(function(){ - setInterval(function(){ - for(var id in players) - { - var player=players[id]; - if(player.type=="merchant" && player.p.stand) - { - var xp=1000; - if(player.level<=40) xp=G.levels[player.level]/100; - else if(player.level<=60) xp=G.levels[player.level]/200; - else if(player.level<=70) xp=G.levels[player.level]/400; - else xp=G.levels[70]/400; - xp=parseInt(Math.round(xp)); - player.xp+=xp; - player.socket.emit("game_log","Gained "+to_pretty_num(xp)+" marketing XP"); - player.socket.emit("disappearing_text",{"message":"+"+xp,"x":player.x,"y":player.y-32,"args":{color:"gray","size":"large"}}); - resend(player,"reopen+u+cid"); + if (player.p && player.p.stand) { + total_merchants += 1; } } - },3*60*60*1000); //3 -},1*60*60*1000); //1 + if (names.length) { + player = get_player(names[floor(Math.random() * names.length)]); + observer_map = player.map; + observer_x = parseInt(player.x); + observer_y = parseInt(player.y); + server_log("Set the observer location: " + [observer_map, observer_x, observer_y]); + } else { + observer_map = merchant_map; + observer_x = merchant_x; + observer_y = merchant_y; + } + } catch (e) { + log_trace("#X observer loop error", e); + } +}, 4000); -setInterval(function(){ - try{ - var edge=future_s(-120); - for(var id in players) - { - if(0 && players[id].last_ipass50) +setTimeout(function () { + setInterval(function () { + try { + for (var id in instances) { + var instance = instances[id]; + if ( + B.pause_instances && + !instance.paused && + !instance.operators && + Object.keys(instance.players).length <= instance.npcs && + !Object.keys(instance.observers).length && + ssince(instance.last_player) > 50 + ) { pause_instance(instance); - if(Object.keys(instance.players).length>instance.npcs || Object.keys(instance.observers).length) - instance.last_player=new Date(); + } + if (Object.keys(instance.players).length > instance.npcs || Object.keys(instance.observers).length) { + instance.last_player = new Date(); + } } - }catch(e){log_trace("#X pause loop error",e);}; - },2000); -},60000); // delay for a minute, so citizens move around + } catch (e) { + log_trace("#X pause loop error", e); + } + }, 2000); +}, 60000); // delay for a minute, so citizens move around -setInterval(function(){ - try{ - for(var oname in parties) - { +setInterval(function () { + try { + for (var oname in parties) { send_party_update(oname); } - }catch(e){log_trace("#X party loop error",e);}; -},60000); + } catch (e) { + log_trace("#X party loop error", e); + } +}, 60000); -setInterval(function(){ - try{ - for(var id in players) - { - var player=players[id]; - players[id].pdps*=0.8; +setInterval(function () { + try { + for (var id in players) { + var player = players[id]; + players[id].pdps *= 0.8; player.p.minutes++; - trade_slots.forEach(function(slot){ - if(player.slots[slot] && player.slots[slot].giveaway) - { + trade_slots.forEach(function (slot) { + if (player.slots[slot] && player.slots[slot].giveaway) { player.slots[slot].giveaway--; - if(!player.slots[slot].giveaway) - { - var list=[]; - player.slots[slot].list.forEach(function(p){ - var p=get_player(p); - if(p && p.esize) list.push(p); - else if(!mode.prevent_external) list.push({name:p}); - }); - if(!list.length) player.slots[slot].giveaway=5; - else - { - if(!mode.prevent_external) - { - - var winner=random_one(list); - var item=player.slots[slot]; - player.slots[slot]=null; - delete item.list; delete item.giveaway; delete item.registry; - item.gf=player.name; item.src="gva"; - var mitem=JSON.stringify(item); - add_to_trade_history(player,"giveaway",winner.name,cache_item(item,true)); - xy_emit(player,"game_log",{message:winner.name+" won "+player.name+"'s giveaway of "+G.items[item.name].name+"!",color:"#42A0DC",confetti:winner.name}); - appengine_call("send_mail",{fro:player.name,to:winner.name,subject:"You've won a giveaway!",message:"Congratulations, you won "+player.name+"'s giveaway. Participants were: "+list.join(", "),rid:randomStr(50),retries:5,item:mitem},function(result){ - - },function(){ - console.log("#M unsent giveaway, lost item: "+mitem); - }); + if (!player.slots[slot].giveaway) { + var list = []; + player.slots[slot].list.forEach(function (p) { + var p = get_player(p); + if (p && p.esize) { + list.push(p); + } else if (!mode.prevent_external) { + list.push({ name: p }); } - else - { - var winner=random_one(list); - var item=player.slots[slot]; - player.slots[slot]=null; - delete item.list; delete item.giveaway; delete item.registry; - item.gf=player.name; item.src="gva"; - add_item(winner,item); - add_to_trade_history(player,"giveaway",winner.name,cache_item(item,true)); - xy_emit(player,"game_log",{message:winner.name+" won "+player.name+"'s giveaway of "+G.items[item.name].name+"!",color:"#42A0DC",confetti:winner.name}); - resend(winner,"reopen"); + }); + if (!list.length) { + player.slots[slot].giveaway = 5; + } else { + if (!mode.prevent_external) { + var winner = random_one(list); + var item = player.slots[slot]; + player.slots[slot] = null; + delete item.list; + delete item.giveaway; + delete item.registry; + item.gf = player.name; + item.src = "gva"; + var mitem = JSON.stringify(item); + add_to_trade_history(player, "giveaway", winner.name, cache_item(item, true)); + xy_emit(player, "game_log", { + message: winner.name + " won " + player.name + "'s giveaway of " + G.items[item.name].name + "!", + color: "#42A0DC", + confetti: winner.name, + }); + appengine_call( + "send_mail", + { + fro: player.name, + to: winner.name, + subject: "You've won a giveaway!", + message: + "Congratulations, you won " + player.name + "'s giveaway. Participants were: " + list.join(", "), + rid: randomStr(50), + retries: 5, + item: mitem, + }, + function (result) {}, + function () { + console.log("#M unsent giveaway, lost item: " + mitem); + }, + ); + } else { + var winner = random_one(list); + var item = player.slots[slot]; + player.slots[slot] = null; + delete item.list; + delete item.giveaway; + delete item.registry; + item.gf = player.name; + item.src = "gva"; + add_item(winner, item); + add_to_trade_history(player, "giveaway", winner.name, cache_item(item, true)); + xy_emit(player, "game_log", { + message: winner.name + " won " + player.name + "'s giveaway of " + G.items[item.name].name + "!", + color: "#42A0DC", + confetti: winner.name, + }); + resend(winner, "reopen"); } } - } - player.cslots[slot]=cache_item(player.slots[slot],true); - resend(player,"u+cid"+(!player.slots[slot]&&"+reopen"||"")); + player.cslots[slot] = cache_item(player.slots[slot], true); + resend(player, "u+cid" + ((!player.slots[slot] && "+reopen") || "")); } }); } - }catch(e){log_trace("#X decay loop error",e);}; -},60000); + } catch (e) { + log_trace("#X decay loop error", e); + } +}, 60000); - -var a_score={}; // announcement score -setInterval(function(){ - try{ - for(var id in a_score) - { - a_score[id]*=0.99; +var a_score = {}; // announcement score +setInterval(function () { + try { + for (var id in a_score) { + a_score[id] *= 0.99; } - }catch(e){log_trace("#X ascore loop error",e);}; -},10000); + } catch (e) { + log_trace("#X ascore loop error", e); + } +}, 10000); -setInterval(function(){ - try{ - for(var id in players) - { - var player=players[id]; - player.xrange=min(25,player.xrange+5); +setInterval(function () { + try { + for (var id in players) { + var player = players[id]; + player.xrange = min(25, player.xrange + 5); } - }catch(e){log_trace("#X xrange loop error",e);}; -},1000); + } catch (e) { + log_trace("#X xrange loop error", e); + } +}, 1000); -setInterval(function(){ - try{ - var c=new Date(); - for(var id in players) - { - var player=players[id]; - if(player.auth_id && player.p.first===undefined && !player.first_u_call) - { - function first_call(player) - { - player.first_u_call=appengine_call("is_first",{auth:player.auth,auth_id:player.auth_id,character:player.real_id,suffix:"/"+player.id},function(result){ - if(result.first) player.p.first=true; - else player.p.first=false; - },function(){ - delete player.first_u_call; - }); +setInterval(function () { + try { + var c = new Date(); + for (var id in players) { + var player = players[id]; + if (player.auth_id && player.p.first === undefined && !player.first_u_call) { + function first_call(player) { + player.first_u_call = appengine_call( + "is_first", + { auth: player.auth, auth_id: player.auth_id, character: player.real_id, suffix: "/" + player.id }, + function (result) { + if (result.first) { + player.p.first = true; + } else { + player.p.first = false; + } + }, + function () { + delete player.first_u_call; + }, + ); } first_call(player); } - if(player.p.first && !player.p.dt.first) - { - realm_broadcast("server_message",{message:player.name+" joined Adventure Land!",color:"#24A2FA"}); - player.p.dt.first=future_h(24*7); - } - else if(player.p.dt.first && player.p.dt.first>c) - { - player.paura=player.paura||{}; - player.paura["newcomersblessing"]={attr0:0,attr1:0}; + if (player.p.first && !player.p.dt.first) { + realm_broadcast("server_message", { message: player.name + " joined Adventure Land!", color: "#24A2FA" }); + player.p.dt.first = future_h(24 * 7); + } else if (player.p.dt.first && player.p.dt.first > c) { + player.paura = player.paura || {}; + player.paura["newcomersblessing"] = { attr0: 0, attr1: 0 }; } } - }catch(e){log_trace("#X first_character loop error",e);}; -},4000); + } catch (e) { + log_trace("#X first_character loop error", e); + } +}, 4000); -function projectiles_loop() -{ - var now=new Date(); - for(var id in projectiles) - { - try{ - if(projectiles[id].eta<=now) - { - var projectile=projectiles[id]; +function projectiles_loop() { + var now = new Date(); + for (var id in projectiles) { + try { + if (projectiles[id].eta <= now) { + var projectile = projectiles[id]; delete projectiles[id]; - complete_attack(projectile.attacker,projectile.target,projectile); + complete_attack(projectile.attacker, projectile.target, projectile); } - }catch(e){log_trace("#X projectile loop error",e);}; + } catch (e) { + log_trace("#X projectile loop error", e); + } } } -setInterval(projectiles_loop,7); +setInterval(projectiles_loop, 7); -var accel=1; -if(mode.fast_mlevels) accel=100; -setInterval(function(){ - try{ - for(var id in instances) - { - for(var mid in (instances[id].monsters||[])) - { - var monster=instances[id].monsters[mid]; - if(G.monsters[monster.type].cute || G.monsters[monster.type].peaceful || G.monsters[monster.type].stationary || monster.target) continue; - var exp=Math.pow(2,(monster.level-1)*0.3),mult=1; - if(monster["1hp"]) mult=200; - else if(G.monsters[monster.type].special) mult=20; - if(mssince(monster.last_level)>max(180000*exp*mult/accel,monster.max_hp*mult*30*exp/accel)) - { - if(monster.temp) remove_monster(monster); - else level_monster(monster); +var accel = 1; +if (mode.fast_mlevels) { + accel = 100; +} +setInterval(function () { + try { + for (var id in instances) { + for (var mid in instances[id].monsters || []) { + var monster = instances[id].monsters[mid]; + if ( + G.monsters[monster.type].cute || + G.monsters[monster.type].peaceful || + G.monsters[monster.type].stationary || + monster.target + ) { + continue; + } + var exp = Math.pow(2, (monster.level - 1) * 0.3); + var mult = 1; + if (monster["1hp"]) { + mult = 200; + } else if (G.monsters[monster.type].special) { + mult = 20; + } + if ( + mssince(monster.last_level) > max((180000 * exp * mult) / accel, (monster.max_hp * mult * 30 * exp) / accel) + ) { + if (monster.temp) { + remove_monster(monster); + } else { + level_monster(monster); + } } } } - }catch(e){log_trace("#X mlevel loop error",e);}; -},16000/accel); + } catch (e) { + log_trace("#X mlevel loop error", e); + } +}, 16000 / accel); -setInterval(function(){ - try{ - for(var id in instances) - { - for(var mid in (instances[id].monsters||[])) - { - var monster=instances[id].monsters[mid]; - if(G.monsters[monster.type].cute || G.monsters[monster.type].stationary || !monster.target) continue; - var target=get_player(monster.target); - if(!target || (target.targets>1 && monster.hp 1 && monster.hp < monster.max_hp * 0.23)) { + continue; + } + monster.extra_gold = + (monster.extra_gold || 0) + + ((min(min(2400, target.attack) / 220.0 + monster.attack / 1.2, 55) + 19) * + ((gameplay == "hardcore" && 100) || 1)) / + (max((target.targets - 1) * (target.targets - 1), 1) || 1); } } - }catch(e){log_trace("#X mgold loop error",e);}; -},2000); + } catch (e) { + log_trace("#X mgold loop error", e); + } +}, 2000); -setInterval(function(){ - try{ - for(var id in players) - { - var player=players[id]; - if(Math.random()<1.0/(6*15)) // once every 15 hours - { - var slots=[]; - character_slots.forEach(function(slot){ - if(player.slots[slot] && (G.items[player.slots[slot].name].upgrade || G.items[player.slots[slot].name].compound)) +setInterval(function () { + try { + for (var id in players) { + var player = players[id]; + if (Math.random() < 1.0 / (6 * 15)) { + // once every 15 hours + var slots = []; + character_slots.forEach(function (slot) { + if ( + player.slots[slot] && + (G.items[player.slots[slot].name].upgrade || G.items[player.slots[slot].name].compound) + ) { slots.push(slot); + } }); - if(!slots.length) continue; - var slot=random_one(slots); - player.slots[slot].grace=(player.slots[slot].grace||0)+0.4; - console.log("random grace "+player.name+" "+slot+" "+player.slots[slot].grace); + if (!slots.length) { + continue; + } + var slot = random_one(slots); + player.slots[slot].grace = (player.slots[slot].grace || 0) + 0.4; + console.log("random grace " + player.name + " " + slot + " " + player.slots[slot].grace); } - if(player.computer && Math.random()<1.0/(6*1)) // once every hour - { - for(var s in player.slots) - { - if(player.slots[s] && G.items[player.slots[s].name].charge) - { - if(player.slots[s].charges>G.items[player.slots[s].name].charge && Math.random()<0.96) continue; - player.slots[s].charges=(player.slots[s].charges||0)+1; - player.cslots[s]=cache_item(player.slots[s]); + if (player.computer && Math.random() < 1.0 / (6 * 1)) { + // once every hour + for (var s in player.slots) { + if (player.slots[s] && G.items[player.slots[s].name].charge) { + if (player.slots[s].charges > G.items[player.slots[s].name].charge && Math.random() < 0.96) { + continue; + } + player.slots[s].charges = (player.slots[s].charges || 0) + 1; + player.cslots[s] = cache_item(player.slots[s]); } } - for(var i=0;iG.items[player.items[i].name].charge && Math.random()<0.96) continue; - player.items[i].charges=(player.items[i].charges||0)+1; - player.citems[i]=cache_item(player.items[i]); + for (var i = 0; i < player.items.length; i++) { + if (player.items[i] && G.items[player.items[i].name].charge) { + if (player.items[i].charges > G.items[player.items[i].name].charge && Math.random() < 0.96) { + continue; + } + player.items[i].charges = (player.items[i].charges || 0) + 1; + player.citems[i] = cache_item(player.items[i]); } } } } - }catch(e){log_trace("#X random grace loop error",e);}; -},10*60000); // every 10 minutes + } catch (e) { + log_trace("#X random grace loop error", e); + } +}, 10 * 60000); // every 10 minutes -setInterval(function(){ - try{ - server_loot(); - }catch(e){log_trace("#X server loot error",e);}; -},4*60*60*1000); - -setInterval(function(){ - try{ - if(server.live) broadcast_e(); - }catch(e){log_trace("#X broadcast error",e);}; -},24*1000); - -function sync_loop() -{ - function check_for_delays(player) - { - var limit=6; - if(player.mounting && msince(player.mounting)>limit && !player.mount_issue) - { - server_log("#X SEVERE: "+limit+" minutes and still mounting: "+player.name,1); - player.mount_issue=new Date(); +setInterval( + function () { + try { + server_loot(); + } catch (e) { + log_trace("#X server loot error", e); } - if(player.unmounting && msince(player.unmounting)>limit && !player.unmount_issue) - { - server_log("#X SEVERE: "+limit+" minutes and still unmounting: "+player.name,1); - player.unmount_issue=new Date(); + }, + 4 * 60 * 60 * 1000, +); + +setInterval(function () { + try { + if (server.live) { + broadcast_e(); } - if(player.sync_call && msince(player.last_sync)>limit && !player.sync_issue) - { - server_log("#X SEVERE: "+limit+" minutes and still syncing: "+player.name,1); + } catch (e) { + log_trace("#X broadcast error", e); + } +}, 24 * 1000); + +function sync_loop() { + function check_for_delays(player) { + var limit = 6; + if (player.mounting && msince(player.mounting) > limit && !player.mount_issue) { + server_log("#X SEVERE: " + limit + " minutes and still mounting: " + player.name, 1); + player.mount_issue = new Date(); + } + if (player.unmounting && msince(player.unmounting) > limit && !player.unmount_issue) { + server_log("#X SEVERE: " + limit + " minutes and still unmounting: " + player.name, 1); + player.unmount_issue = new Date(); + } + if (player.sync_call && msince(player.last_sync) > limit && !player.sync_issue) { + server_log("#X SEVERE: " + limit + " minutes and still syncing: " + player.name, 1); // player.sync_issue=new Date(); delete player.sync_call; } - if(player.stopping && msince(player.stopping)>limit && !player.stop_issue) - { - server_log("#X SEVERE: "+limit+" minutes and still stopping: "+player.name,1); - player.stop_issue=new Date(); + if (player.stopping && msince(player.stopping) > limit && !player.stop_issue) { + server_log("#X SEVERE: " + limit + " minutes and still stopping: " + player.name, 1); + player.stop_issue = new Date(); } } - function mount_call(player) - { + function mount_call(player) { // player.last_sync=new Date(); - sync doesn't happen at mount - maybe it should [16/08/17] - player.mount_call=appengine_call("mount_user",{auth:player.auth,character:player.real_id,suffix:"/"+player.id,to:player.mount_to},function(result){ - server_log("mount_user: "+player.name+" owner: "+player.owner,1); - delete player.mounting; - delete player.mount_call; - if(result.failed) - { - server_log("mount_user[failed]: "+player.name+" in-bank: "+result.name+" result: "+JSON.stringify(result),1); - player.socket.emit("game_response",{response:"bank_opx",name:result.name,reason:result.reason}); - return; - } - player.user=result.user; - init_bank(player); - if(players[player.socket.id]) - { - transport_player_to(player,player.mount_to,player.mount_s); - resend(player); - } - },function(){ - delete player.mounting; - delete player.mount_call; - server_log("#X SEVERE-ish: Mount failed for "+player.name,1); - }); + player.mount_call = appengine_call( + "mount_user", + { auth: player.auth, character: player.real_id, suffix: "/" + player.id, to: player.mount_to }, + function (result) { + server_log("mount_user: " + player.name + " owner: " + player.owner, 1); + delete player.mounting; + delete player.mount_call; + if (result.failed) { + server_log( + "mount_user[failed]: " + player.name + " in-bank: " + result.name + " result: " + JSON.stringify(result), + 1, + ); + player.socket.emit("game_response", { response: "bank_opx", name: result.name, reason: result.reason }); + return; + } + player.user = result.user; + init_bank(player); + if (players[player.socket.id]) { + transport_player_to(player, player.mount_to, player.mount_s); + resend(player); + } + }, + function () { + delete player.mounting; + delete player.mount_call; + server_log("#X SEVERE-ish: Mount failed for " + player.name, 1); + }, + ); } - function unmount_call(player) - { - player.last_sync=new Date(); + function unmount_call(player) { + player.last_sync = new Date(); init_bank_exit(player); - player.unmount_call=appengine_call("sync_character",{auth:player.auth,character:player.real_id,data:player_to_server(player,"sync"),user_data:player.user||"",retries:100,unmount:1,suffix:"/"+player.name+"/unmount" - },function(result){ - server_log("unmount_user[sync]: "+player.name+" owner: "+player.owner+" result: "+JSON.stringify(result),1); - delete player.unmount_call; - if(result.failed) { server_log("unmount_user[failed]: "+player.name+" owner: "+player.owner,1); return; } - delete player.unmounting; - player.user=null; player.cuser=null; - if(players[player.socket.id] && player.unmount_to) + player.unmount_call = appengine_call( + "sync_character", { - transport_player_to(player,player.unmount_to,player.unmount_s); - } - if(players[player.socket.id]) resend(player); - },function(){ - server_log("#X SEVERE: Unmount failed for "+player.name,1); - delete player.unmounting; - delete player.unmount_call; - if(players[player.socket.id]) players[socket.id].socket.disconnect(); - }); + auth: player.auth, + character: player.real_id, + data: player_to_server(player, "sync"), + user_data: player.user || "", + retries: 100, + unmount: 1, + suffix: "/" + player.name + "/unmount", + }, + function (result) { + server_log( + "unmount_user[sync]: " + player.name + " owner: " + player.owner + " result: " + JSON.stringify(result), + 1, + ); + delete player.unmount_call; + if (result.failed) { + server_log("unmount_user[failed]: " + player.name + " owner: " + player.owner, 1); + return; + } + delete player.unmounting; + player.user = null; + player.cuser = null; + if (players[player.socket.id] && player.unmount_to) { + transport_player_to(player, player.unmount_to, player.unmount_s); + } + if (players[player.socket.id]) { + resend(player); + } + }, + function () { + server_log("#X SEVERE: Unmount failed for " + player.name, 1); + delete player.unmounting; + delete player.unmount_call; + if (players[player.socket.id]) { + players[socket.id].socket.disconnect(); + } + }, + ); } - function sync_call(player) - { - player.last_sync=new Date(); - player.sync_call=appengine_call("sync_character",{auth:player.auth,character:player.real_id,data:player_to_server(player,"sync"),user_data:player.user||"",retries:0,suffix:"/"+player.name}, - function(result){ - if(result && result.reason=="notingame") - { - server_log("#X SEVERE: sync notingame disconnect for "+player.name,1); - try{player.socket.disconnect();}catch(e){}; + function sync_call(player) { + player.last_sync = new Date(); + player.sync_call = appengine_call( + "sync_character", + { + auth: player.auth, + character: player.real_id, + data: player_to_server(player, "sync"), + user_data: player.user || "", + retries: 0, + suffix: "/" + player.name, + }, + function (result) { + if (result && result.reason == "notingame") { + server_log("#X SEVERE: sync notingame disconnect for " + player.name, 1); + try { + player.socket.disconnect(); + } catch (e) {} } delete player.sync_call; }, - function(){delete player.sync_call}); + function () { + delete player.sync_call; + }, + ); } - function stop_call(player) - { - var bank=false; - if(player.user) bank=true; - player.stopping=new Date(); + function stop_call(player) { + var bank = false; + if (player.user) { + bank = true; + } + player.stopping = new Date(); init_player_exit(player); - if(player.unmount_call) player.unmount_call.retries=0; - player.stop_call=appengine_call("stop_character",{auth:player.auth,character:player.real_id,data:player_to_server(player),user_data:player.user||"",retries:CINF,suffix:"/"+player.name},function(result){ - server_log("stop_character: "+player.name+" owner: "+player.owner+" bank: "+bank+" result: "+JSON.stringify(result),1); - if(result.done) delete dc_players[player.real_id]; - delete player.stop_call; - },function(){ - server_log("#X SEVERE: stop_character failed for "+player.name,1); - delete player.stop_call; - }); + if (player.unmount_call) { + player.unmount_call.retries = 0; + } + player.stop_call = appengine_call( + "stop_character", + { + auth: player.auth, + character: player.real_id, + data: player_to_server(player), + user_data: player.user || "", + retries: CINF, + suffix: "/" + player.name, + }, + function (result) { + server_log( + "stop_character: " + + player.name + + " owner: " + + player.owner + + " bank: " + + bank + + " result: " + + JSON.stringify(result), + 1, + ); + if (result.done) { + delete dc_players[player.real_id]; + } + delete player.stop_call; + }, + function () { + server_log("#X SEVERE: stop_character failed for " + player.name, 1); + delete player.stop_call; + }, + ); } - for(var id in players) - { - if(gameplay=="hardcore" || gameplay=="test") return; - var player=players[id]; - try{ - + for (var id in players) { + if (gameplay == "hardcore" || gameplay == "test") { + return; + } + var player = players[id]; + try { check_for_delays(player); - if(player.unmount_call || player.mount_call || player.sync_call || player.stop_call); - else if(!server.live) player.socket.disconnect(); - else if(player.unmounting) unmount_call(player); - else if(player.mounting) mount_call(player); - else if(msince(player.last_sync)>5 && !player.unmounting && !player.mounting) sync_call(player); - - }catch(e){log_trace("#X dc loop error1",e);}; + if (player.unmount_call || player.mount_call || player.sync_call || player.stop_call) { + } else if (!server.live) { + player.socket.disconnect(); + } else if (player.unmounting) { + unmount_call(player); + } else if (player.mounting) { + mount_call(player); + } else if (msince(player.last_sync) > 5 && !player.unmounting && !player.mounting) { + sync_call(player); + } + } catch (e) { + log_trace("#X dc loop error1", e); + } } - for(var id in dc_players) - { - if(gameplay=="hardcore" || gameplay=="test") return; - var player=dc_players[id]; - try{ - + for (var id in dc_players) { + if (gameplay == "hardcore" || gameplay == "test") { + return; + } + var player = dc_players[id]; + try { check_for_delays(player); - if(player.unmount_call || player.mount_call || player.sync_call || player.stop_call); - else if(!player.stop_call) stop_call(player); - - }catch(e){log_trace("#X dc loop error2",e);}; + if (player.unmount_call || player.mount_call || player.sync_call || player.stop_call) { + } else if (!player.stop_call) { + stop_call(player); + } + } catch (e) { + log_trace("#X dc loop error2", e); + } } } -function server_loop() -{ +function server_loop() { // #IMPORTANT: sometimes none of the success/error callbacks trigger [20/08/19] - if(server.update_call && msince(server.update_call.init)>4) + if (server.update_call && msince(server.update_call.init) > 4) { delete server.update_call; - if(server.update_call || server.stop_call); - else if(server.live && ssince(server.last_update)>75) - { - server.update_call=appengine_call("update_server",{ - keyword:variables.keyword, - id:server_id, - players:Object.keys(players).length, - observers:Object.keys(observers).length, - merchants:total_merchants, - total_players:total_players, - data:S, - retries:3, - },function(result){ - server.last_update=new Date(); + } + if (server.update_call || server.stop_call) { + } else if (server.live && ssince(server.last_update) > 75) { + server.update_call = appengine_call( + "update_server", + { + keyword: variables.keyword, + id: server_id, + players: Object.keys(players).length, + observers: Object.keys(observers).length, + merchants: total_merchants, + total_players: total_players, + data: S, + retries: 3, + }, + function (result) { + server.last_update = new Date(); server_log("Server update sent"); delete server.update_call; - },function(){ - server_log("Server update failed",1); + }, + function () { + server_log("Server update failed", 1); delete server.update_call; - }); + }, + ); + } else if (server.started && !server.live && !server.stopped) { + server.stop_call = appengine_call( + "stop_server", + { keyword: variables.keyword, id: server_id, retries: CINF, data: S }, + function (result) { + server_log("stop_server success", 1); + // these are here as shutting the server first disrupts connections in actual servers + // io.close(); app.close(); - decided to comment these out, they don't do anything now [23/09/16] + server.stopped = true; + delete server.stop_call; + }, + ); + } else if ( + server.stopped && + ((!Object.keys(dc_players).length && !Object.keys(players).length) || gameplay == "hardcore" || gameplay == "test") + ) { + process.exit(); + } else if (server.stopped) { + sync_loop(); } - else if(server.started && !server.live && !server.stopped) - { - server.stop_call=appengine_call("stop_server",{keyword:variables.keyword,id:server_id,retries:CINF,data:S},function(result){ - server_log("stop_server success",1); - // these are here as shutting the server first disrupts connections in actual servers - // io.close(); app.close(); - decided to comment these out, they don't do anything now [23/09/16] - server.stopped=true; - delete server.stop_call; - }); - } - else if(server.stopped && (!Object.keys(dc_players).length && !Object.keys(players).length || (gameplay=="hardcore" || gameplay=="test"))) process.exit(); - else if(server.stopped) sync_loop(); } -setInterval(sync_loop,24000); -setInterval(server_loop,1000); +setInterval(sync_loop, 24000); +setInterval(server_loop, 1000); -function shutdown() -{ - server_log("shutdown",1); - server.live=false; - if(!server.exists) server_loot("all"); +function shutdown() { + server_log("shutdown", 1); + server.live = false; + if (!server.exists) { + server_loot("all"); + } sync_loop(); } -function shutdown_routine() -{ - server_log("shutdown_routine",1); - server.shutdown=true; - for(var name in instances) - { - for(var id in instances[name].monsters) - { - var monster=instances[name].monsters[id]; - if(monster.target) - { - monster.u=true; +function shutdown_routine() { + server_log("shutdown_routine", 1); + server.shutdown = true; + for (var name in instances) { + for (var id in instances[name].monsters) { + var monster = instances[name].monsters[id]; + if (monster.target) { + monster.u = true; monster.cid++; - monster.mult=(monster.max_hp-monster.hp)/monster.max_hp; - monster.hp=1; + monster.mult = (monster.max_hp - monster.hp) / monster.max_hp; + monster.hp = 1; } } } - var seconds=20; - if(is_sdk) seconds=1; - else if(gameplay=="hardcore") seconds=240; - workers.forEach(function(worker){ - try{worker.postMessage({type:"exit"});} - catch(e){console.log(e);} + var seconds = 20; + if (is_sdk) { + seconds = 1; + } else if (gameplay == "hardcore") { + seconds = 240; + } + workers.forEach(function (worker) { + try { + worker.postMessage({ type: "exit" }); + } catch (e) { + console.log(e); + } }); - for(var i=0;itotal/D.drops[n].length) D.drops[n][i][0]/=12.0; - else if(D.drops[n][i][0]/1.5>total/D.drops[n].length) D.drops[n][i][0]/=3.0; + for (var n in D.drops) { + if (!is_array(D.drops[n])) { + continue; + } + var total = 0; + for (var i = 0; i < D.drops[n].length; i++) { + total += D.drops[n][i][0]; + } + for (var i = 0; i < D.drops[n].length; i++) { + if (D.drops[n][i][0] < 0.001) { + D.drops[n][i][0] *= 200; + } + + if (D.drops[n][i][0] * 3 < total / D.drops[n].length) { + D.drops[n][i][0] *= 12.0; + } else if (D.drops[n][i][0] * 1.5 < total / D.drops[n].length) { + D.drops[n][i][0] *= 3.0; + } else if (D.drops[n][i][0] / 3 > total / D.drops[n].length) { + D.drops[n][i][0] /= 12.0; + } else if (D.drops[n][i][0] / 1.5 > total / D.drops[n].length) { + D.drops[n][i][0] /= 3.0; + } } } - for(var mname in G.maps) - { - if(!D.drops.maps[mname]) D.drops.maps[mname]=[]; - - for(var i=0;i0.08 && def.hp>900) pack.gold*=0.7; // if(def.hp>=50000) pack.gold*=0.90; // better dps utilisation - don't punish this - pack.gold*=def.difficulty||1; - pack.gold=parseInt(ceil(pack.gold))||0; - if(def.stationary || def.xp<0) pack.gold=D.monster_gold[pack.type]||0; - D.base_gold[pack.type]=D.base_gold[pack.type]||{}; - D.base_gold[pack.type][mname]=pack.gold; - console.log(mname+" "+pack.type+" gold: "+pack.gold+"["+parseInt(drop_value)+","+parseInt(map_value)+"] gold/hp: "+(pack.gold/def.hp)+" total%: "+((pack.gold+drop_value+map_value)/def.hp)); + pack.gold *= def.difficulty || 1; + pack.gold = parseInt(ceil(pack.gold)) || 0; + if (def.stationary || def.xp < 0) { + pack.gold = D.monster_gold[pack.type] || 0; + } + D.base_gold[pack.type] = D.base_gold[pack.type] || {}; + D.base_gold[pack.type][mname] = pack.gold; + console.log( + mname + + " " + + pack.type + + " gold: " + + pack.gold + + "[" + + parseInt(drop_value) + + "," + + parseInt(map_value) + + "] gold/hp: " + + pack.gold / def.hp + + " total%: " + + (pack.gold + drop_value + map_value) / def.hp, + ); }); } - for(var sname in G.sets) - { - var set=G.sets[sname]; - for(var i=2;i<=set.items.length;i++) - { - set[i]=set[i]||{}; - for(var prop in set[i-1]) - { - if(set[i][prop]) set[i][prop]+=set[i-1][prop]; - else set[i][prop]=set[i-1][prop]; + for (var sname in G.sets) { + var set = G.sets[sname]; + for (var i = 2; i <= set.items.length; i++) { + set[i] = set[i] || {}; + for (var prop in set[i - 1]) { + if (set[i][prop]) { + set[i][prop] += set[i - 1][prop]; + } else { + set[i][prop] = set[i - 1][prop]; + } } } } - if(is_pvp) - { - D.drops.maps.global_static.push([1.0/(gameplay=="hardcore"&&1000||100000),"pvptoken"]); + if (is_pvp) { + D.drops.maps.global_static.push([1.0 / ((gameplay == "hardcore" && 1000) || 100000), "pvptoken"]); } - if(events.halloween) - { - G.monsters.jr.respawn=480; - G.monsters.greenjr.respawn=480; - D.drops.maps.global.push([0.00005,"candy0"]); - D.drops.maps.global.push([0.00125,"candy1"]); + if (events.halloween) { + G.monsters.jr.respawn = 480; + G.monsters.greenjr.respawn = 480; + D.drops.maps.global.push([0.00005, "candy0"]); + D.drops.maps.global.push([0.00125, "candy1"]); } - if(events.holidayseason) - { - events.snowman=60; + if (events.holidayseason) { + events.snowman = 60; } } -function calculate_xvalue(arr,rec,divide,mult) -{ - var value=0,total=0; +function calculate_xvalue(arr, rec, divide, mult) { + var value = 0; + var total = 0; // console.log(arr); - arr.forEach(function(drop){ - total+=drop[0]; + arr.forEach(function (drop) { + total += drop[0]; }); - if(!mult) mult=1; - if(!divide) total=1; - else mult=1; - arr.forEach(function(drop){ - if(drop[1]=="open") value+=min(1,drop[0]*mult/total)*calculate_xvalue(D.drops[drop[2]],1,1,mult); - else if(drop[1]=="shells") value+=min(1,drop[0]*mult/total)*drop[2]*G.multipliers.shells_to_gold; - else if(drop[1]=="empty"); - else if(drop[1]=="gold") value+=drop[2]*min(1,drop[0]*mult/total); - else - { - var def=G.items[drop[1]],q=drop[2]||1; - var g=def.g,avalue=def.g; - if(def.e && !rec) - { - var suffix=""; - if(def.upgrade || def.compound) suffix="0"; - avalue=calculate_xvalue(D.drops[drop[1]+suffix],1,1,mult)/def.e; + if (!mult) { + mult = 1; + } + if (!divide) { + total = 1; + } else { + mult = 1; + } + arr.forEach(function (drop) { + if (drop[1] == "open") { + value += min(1, (drop[0] * mult) / total) * calculate_xvalue(D.drops[drop[2]], 1, 1, mult); + } else if (drop[1] == "shells") { + value += min(1, (drop[0] * mult) / total) * drop[2] * G.multipliers.shells_to_gold; + } else if (drop[1] == "empty") { + } else if (drop[1] == "gold") { + value += drop[2] * min(1, (drop[0] * mult) / total); + } else { + var def = G.items[drop[1]]; + var q = drop[2] || 1; + var g = def.g; + var avalue = def.g; + if (def.e && !rec) { + var suffix = ""; + if (def.upgrade || def.compound) { + suffix = "0"; + } + avalue = calculate_xvalue(D.drops[drop[1] + suffix], 1, 1, mult) / def.e; // console.log(drop[1]+" value: "+g+" actual: "+avalue); } - value+=q*min(1,drop[0]*mult/total)*(g+avalue)/2; + value += (q * min(1, (drop[0] * mult) / total) * (g + avalue)) / 2; + } + if (!value) { + console.log(drop); } - if(!value) console.log(drop); }); return value; } -function add_to_trade_history(player,event,name,item,price) -{ - if(!player.p.trade_history) player.p.trade_history=[]; - var last=player.p.trade_history[player.p.trade_history.length-1]; - if(last && last[0]==event && last[1]==name && last[2].name==item.name && last[2].level==item.level) - { - last[2].q=(last[2].q||1)+(item.q||1); - last[3]+=price; +function add_to_trade_history(player, event, name, item, price) { + if (!player.p.trade_history) { + player.p.trade_history = []; + } + var last = player.p.trade_history[player.p.trade_history.length - 1]; + if (last && last[0] == event && last[1] == name && last[2].name == item.name && last[2].level == item.level) { + last[2].q = (last[2].q || 1) + (item.q || 1); + last[3] += price; return; } - if(player.p.trade_history.length>=40) player.p.trade_history.shift(); - player.p.trade_history.push([event,name,item,price]); + if (player.p.trade_history.length >= 40) { + player.p.trade_history.shift(); + } + player.p.trade_history.push([event, name, item, price]); } -function add_to_history(player,event) -{ - if(!player.p.history) player.p.history=[]; - if(player.p.history.length>=400) player.p.history.shift(); +function add_to_history(player, event) { + if (!player.p.history) { + player.p.history = []; + } + if (player.p.history.length >= 400) { + player.p.history.shift(); + } player.p.history.push(event); } -function is_free(player) -{ - if(player.bot || player.p.free || player.s.licenced || player.role=="gm") return true; +function is_free(player) { + if (player.bot || player.p.free || player.s.licenced || player.role == "gm") { + return true; + } return false; } -function is_player_allowed(player) -{ - if(gameplay=="hardcore") - { +function is_player_allowed(player) { + if (gameplay == "hardcore") { //if(player.type=="mage" || player.type=="priest") return false; //if(player.type=="rogue") return false; } - var socket=player.socket,characters=0,ips=0,ipx=player.ipx||1,auths=0; - if(player.temp_auth || player.auth_id) ipx=12; - if(is_free(player) || player.stones && 0) return true; - if(player.type=="merchant") - { - for(var id in players) - { - if(players[id].stones && 0 || id==player.socket.id || is_free(player)) continue; - if(players[id].owner==player.owner && players[id].type=="merchant") return false; + var socket = player.socket; + var characters = 0; + var ips = 0; + var ipx = player.ipx || 1; + var auths = 0; + if (player.temp_auth || player.auth_id) { + ipx = 12; + } + if (is_free(player) || (player.stones && 0)) { + return true; + } + if (player.type == "merchant") { + for (var id in players) { + if ((players[id].stones && 0) || id == player.socket.id || is_free(player)) { + continue; + } + if (players[id].owner == player.owner && players[id].type == "merchant") { + return false; + } } return true; } - for(var id in players) - { - if(players[id].stones && 0 || is_free(players[id]) || players[id].type=="merchant") continue; // was requested [28/10/16] - if(players[id].owner==player.owner) characters++; - if(players[id].name==player.name && player!=players[id]) return false; // "hardcore" - if(get_ip(players[id])==get_ip(player)) ips++; - if(player.auth_id && players[id].auth_id==player.auth_id) auths++; - if(auths>variables.character_limit) return false; - if(characters>variables.character_limit) return false; - if(ips>variables.ip_limit*ipx) return false; + for (var id in players) { + if ((players[id].stones && 0) || is_free(players[id]) || players[id].type == "merchant") { + continue; + } // was requested [28/10/16] + if (players[id].owner == player.owner) { + characters++; + } + if (players[id].name == player.name && player != players[id]) { + return false; + } // "hardcore" + if (get_ip(players[id]) == get_ip(player)) { + ips++; + } + if (player.auth_id && players[id].auth_id == player.auth_id) { + auths++; + } + if (auths > variables.character_limit) { + return false; + } + if (characters > variables.character_limit) { + return false; + } + if (ips > variables.ip_limit * ipx) { + return false; + } } return true; } -function rip(player) -{ - player.hp=0; - player.rip=true; - player.rip_time=new Date(); - player.moving=false; - player.abs=true; - if(player.party) send_party_update(player.party); +function rip(player) { + player.hp = 0; + player.rip = true; + player.rip_time = new Date(); + player.moving = false; + player.abs = true; + if (player.party) { + send_party_update(player.party); + } } -function notify_friends(data) -{ - data.list.forEach(function(name){ - var player=players[name_to_id[name]]; if(!player) return; - player.socket.emit("online",{name:data.name,server:data.server}); +function notify_friends(data) { + data.list.forEach(function (name) { + var player = players[name_to_id[name]]; + if (!player) { + return; + } + player.socket.emit("online", { name: data.name, server: data.server }); }); } -function check_player(player) // to check players in setTimeout's -{ - if(!player || !player.socket || player.dc || !players[player.socket.id]) return false; +function check_player(player) { + // to check players in setTimeout's + if (!player || !player.socket || player.dc || !players[player.socket.id]) { + return false; + } return true; } -function is_invis(player) -{ - if(player.s && (player.s.invis || player.s.ethereal)) return true; +function is_invis(player) { + if (player.s && (player.s.invis || player.s.ethereal)) { + return true; + } return false; } -function is_invinc(player) -{ - if(player.s && (player.s.invis || player.s.ethereal || player.s.invincible)) return true; +function is_invinc(player) { + if (player.s && (player.s.invis || player.s.ethereal || player.s.invincible)) { + return true; + } return false; } -function is_in_pvp(player,allow_safe) -{ - if(allow_safe && G.maps[player.map].safe) return false; - if(is_pvp || G.maps[player.map].pvp) return true; +function is_in_pvp(player, allow_safe) { + if (allow_safe && G.maps[player.map].safe) { + return false; + } + if (is_pvp || G.maps[player.map].pvp) { + return true; + } return false; } -function is_map_pvp(map,allow_safe) -{ - if(allow_safe && G.maps[map].safe) return false; - if(is_pvp || G.maps[map].pvp) return true; +function is_map_pvp(map, allow_safe) { + if (allow_safe && G.maps[map].safe) { + return false; + } + if (is_pvp || G.maps[map].pvp) { + return true; + } return false; } -function is_same(player1,player2,party) -{ - if(is_sdk && player1.name!=player2.name) return false; - if(player1.name==player2.name) return true; - if(player1.owner && player1.owner==player2.owner || !is_sdk && get_ip(player1)==get_ip(player2)) return true; - if(party==3 && !is_in_pvp(player1)) return true; - if(party && player1.s && player1.s.coop && player2.s && player2.s.coop && player1.s.coop.id==player2.s.coop.id) return true; // previously party==2 [08/05/21] - if(party && player1.party && player1.party==player2.party) return true; - if(party && player1.team && player1.team==player2.team) return true; +function is_same(player1, player2, party) { + if (is_sdk && player1.name != player2.name) { + return false; + } + if (player1.name == player2.name) { + return true; + } + if ((player1.owner && player1.owner == player2.owner) || (!is_sdk && get_ip(player1) == get_ip(player2))) { + return true; + } + if (party == 3 && !is_in_pvp(player1)) { + return true; + } + if (party && player1.s && player1.s.coop && player2.s && player2.s.coop && player1.s.coop.id == player2.s.coop.id) { + return true; + } // previously party==2 [08/05/21] + if (party && player1.party && player1.party == player2.party) { + return true; + } + if (party && player1.team && player1.team == player2.team) { + return true; + } return false; } -function decay_s(player,ms) -{ - for(var name in (player.s||{})) - { - if(G.conditions[name] && G.conditions[name].debuff) continue; - if(player.s[name].ms<60000 || player.s[name].citizens) - { - if(player.s[name].citizens) - { - for(var p in player.s[name]) - { - if(p!="ms" && is_number(player.s[name][p])) - player.s[name][p]=round(player.s[name][p]/4.0); +function decay_s(player, ms) { + for (var name in player.s || {}) { + if (G.conditions[name] && G.conditions[name].debuff) { + continue; + } + if (player.s[name].ms < 60000 || player.s[name].citizens) { + if (player.s[name].citizens) { + for (var p in player.s[name]) { + if (p != "ms" && is_number(player.s[name][p])) { + player.s[name][p] = round(player.s[name][p] / 4.0); + } } } - player.s[name].ms-=ms; + player.s[name].ms -= ms; } } } -function reset_player(player,soft) -{ - if(!player.p.hardcore) - { - player.p={hardcore:true,dt:{}}; - player.max_stats={monsters:{}}; +function reset_player(player, soft) { + if (!player.p.hardcore) { + player.p = { hardcore: true, dt: {} }; + player.max_stats = { monsters: {} }; } - player.xp=0; - if(soft) - { + player.xp = 0; + if (soft) { // player.level=max(1,player.level-20); - } - else - { - player.level=1; - player.xp=0; - player.s={}; - player.slots={}; - player.items=[{"name":"computer"},{"name":"tracker"}]; - player.gold=0; - if(P[player.real_id] && gameplay!="test") - { - for(var p in P[player.real_id]) player[p]=P[player.real_id][p]; - P[player.real_id]=null; + } else { + player.level = 1; + player.xp = 0; + player.s = {}; + player.slots = {}; + player.items = [{ name: "computer" }, { name: "tracker" }]; + player.gold = 0; + if (P[player.real_id] && gameplay != "test") { + for (var p in P[player.real_id]) { + player[p] = P[player.real_id][p]; + } + P[player.real_id] = null; } } } -function save_player(player) -{ - P[player.real_id]={ - level:player.level, - xp:player.xp, - slots:player.slots, - items:player.items, - gold:player.gold, - x:player.x, - y:player.y, - map:player.map, - in:player.in, - rip:player.rip, - kills:player.kills, - p:player.p, - name:player.name, - auth_id:player.auth_id, +function save_player(player) { + P[player.real_id] = { + level: player.level, + xp: player.xp, + slots: player.slots, + items: player.items, + gold: player.gold, + x: player.x, + y: player.y, + map: player.map, + in: player.in, + rip: player.rip, + kills: player.kills, + p: player.p, + name: player.name, + auth_id: player.auth_id, }; } -function secondhands_logic(item,quantity) -{ - var new_item=cache_item(item),done=false,count=0,replace=null; - if(!(!G.items[item.name].buy || G.items[item.name].upgrade && item.level>=7 || G.items[item.name].compound && item.level>=2) || G.items[item.name].cash || item.expires || item.acl) return; - if(item.grace!==undefined) new_item.grace=item.grace; - if(new_item.q!==undefined) new_item.q=quantity; +function secondhands_logic(item, quantity) { + var new_item = cache_item(item); + var done = false; + var count = 0; + var replace = null; + if ( + !( + !G.items[item.name].buy || + (G.items[item.name].upgrade && item.level >= 7) || + (G.items[item.name].compound && item.level >= 2) + ) || + G.items[item.name].cash || + item.expires || + item.acl + ) { + return; + } + if (item.grace !== undefined) { + new_item.grace = item.grace; + } + if (new_item.q !== undefined) { + new_item.q = quantity; + } delete new_item.v; - new_item.rid=randomStr(5); - for(var i=0;i=5) current=replace; - S.sold[current]=new_item; - csold[current]=cache_item(S.sold[current],true); + if (!done && (count < 5 || (new_item.p && replace !== null))) { + var current = S.sold.length % 400; + if (count >= 5) { + current = replace; + } + S.sold[current] = new_item; + csold[current] = cache_item(S.sold[current], true); } } -function lostandfound_logic(item) -{ - var done=false,count=0,replace=null; - item.rid=randomStr(5); - for(var i=0;i=5) current=replace; - S.found[current]=item; - cfound[current]=cache_item(S.found[current],true); + if (!done && (count < 5 || (item.p && replace !== null))) { + var current = S.found.length % 400; + if (count >= 5) { + current = replace; + } + S.found[current] = item; + cfound[current] = cache_item(S.found[current], true); } } -function server_loot(type) -{ - if(type=="all") - { - for(var mtype in D.drops.monsters) - for(var j=0;j0.1) D.drops.monsters[mtype][j][0]=0.05; - for(var id in instances) - { - if(instances[id].map!=instances[id].name) continue; - for(var mid in instances[id].monsters) - if(instances[id].monsters[mid].frequency<4 && instances.main) - drop_something(instances.main.players[NPC_prefix+"Kane"],instances[id].monsters[mid]); +function server_loot(type) { + if (type == "all") { + for (var mtype in D.drops.monsters) { + for (var j = 0; j < D.drops.monsters[mtype].length; j++) { + if (D.drops.monsters[mtype][j][0] > 0.1) { + D.drops.monsters[mtype][j][0] = 0.05; + } + } + } + for (var id in instances) { + if (instances[id].map != instances[id].name) { + continue; + } + for (var mid in instances[id].monsters) { + if (instances[id].monsters[mid].frequency < 4 && instances.main) { + drop_something(instances.main.players[NPC_prefix + "Kane"], instances[id].monsters[mid]); + } + } } } - for(var id in chests) - { - var chest=chests[id]; - if(type!="all" && hsince(chests[id].date)<48) return; + for (var id in chests) { + var chest = chests[id]; + if (type != "all" && hsince(chests[id].date) < 48) { + return; + } delete chests[id]; - S.gold+=chest.gold; - if(chest.cash) S.cash+=chest.cash; - if(chest.items) chest.items.forEach(function(item){ - lostandfound_logic(item); - }); - if(chest.pvp_items) chest.pvp_items.forEach(function(item){ - lostandfound_logic(item); - }); + S.gold += chest.gold; + if (chest.cash) { + S.cash += chest.cash; + } + if (chest.items) { + chest.items.forEach(function (item) { + lostandfound_logic(item); + }); + } + if (chest.pvp_items) { + chest.pvp_items.forEach(function (item) { + lostandfound_logic(item); + }); + } } } -function server_tax(gold,preview) -{ - var tax=0; - if(1) tax=parseInt(gold*0.1); - else if(S.gold<500000000) tax=parseInt(gold*0.25); - else if(S.gold<1000000000) tax=parseInt(gold*0.5); - if(!preview) S.gold+=tax; - return gold-tax; +function server_tax(gold, preview) { + var tax = 0; + if (1) { + tax = parseInt(gold * 0.1); + } else if (S.gold < 500000000) { + tax = parseInt(gold * 0.25); + } else if (S.gold < 1000000000) { + tax = parseInt(gold * 0.5); + } + if (!preview) { + S.gold += tax; + } + return gold - tax; } -function merchant_xp_logic(player,seller,price,tax) -{ - if(is_same(player,seller)) return; - if(!player.p.xpcache || Object.keys(player.p.xpcache).length>120 || hsince(player.p.dt.last_xpcache)>5*24) player.p.xpcache={},player.p.dt.last_xpcache=new Date(); - if(!player.p.xpcache[seller.name]) player.p.xpcache[seller.name]=0; - var initial=player.p.xpcache[seller.name]; - player.p.xpcache[seller.name]=min(120000000,player.p.xpcache[seller.name]+round(price/4)); - player.xp+=player.p.xpcache[seller.name]-initial; +function merchant_xp_logic(player, seller, price, tax) { + if (is_same(player, seller)) { + return; + } + if (!player.p.xpcache || Object.keys(player.p.xpcache).length > 120 || hsince(player.p.dt.last_xpcache) > 5 * 24) { + player.p.xpcache = {}; + player.p.dt.last_xpcache = new Date(); + } + if (!player.p.xpcache[seller.name]) { + player.p.xpcache[seller.name] = 0; + } + var initial = player.p.xpcache[seller.name]; + player.p.xpcache[seller.name] = min(120000000, player.p.xpcache[seller.name] + round(price / 4)); + player.xp += player.p.xpcache[seller.name] - initial; } -function merchant_xp_logic(player,seller,price,tax) -{ - if(is_same(player,seller)) return; - player.xp+=tax*3.2; +function merchant_xp_logic(player, seller, price, tax) { + if (is_same(player, seller)) { + return; + } + player.xp += tax * 3.2; } -function normalise(data) // normalise(data,["gold","gold"],["num","inv"]) -{ - for(var i=1;inew Date()) - { - player.auth_type="mas"; - player.auth_id=player.p.mas_auth_id; +function verify_mas_receipt(player, receipt) { + if (player.p.mas_hash == quick_hash(receipt) && player.p.dt.mas_expire > new Date()) { + player.auth_type = "mas"; + player.auth_id = player.p.mas_auth_id; delete player.s.authfail; - } - else - { - console.log("verify_mas_receipt for "+player.name); - player.temp_auth="mas"; + } else { + console.log("verify_mas_receipt for " + player.name); + player.temp_auth = "mas"; //player.mas_receipt=receipt; - var content={'receipt-data':receipt,password:variables.apple_token}; - var url="https://buy.itunes.apple.com/verifyReceipt"; + var content = { "receipt-data": receipt, password: variables.apple_token }; + var url = "https://buy.itunes.apple.com/verifyReceipt"; request( { - url:url, - method:"POST", - json:content, + url: url, + method: "POST", + json: content, }, - function(err,response,body){ - if(err) - { - console.log("#M: mas error: "+player.name+" - "+err); + function (err, response, body) { + if (err) { + console.log("#M: mas error: " + player.name + " - " + err); } - if(body && body.status==0 && body.receipt.download_id && body.receipt.original_purchase_date_ms) - { + if (body && body.status == 0 && body.receipt.download_id && body.receipt.original_purchase_date_ms) { delete player.temp_auth; - player.auth_type="mas"; - player.auth_id=(""+body.receipt.download_id).substr(0,4)+body.receipt.original_purchase_date_ms; - player.p.mas_auth_id=player.auth_id; - player.p.mas_hash=quick_hash(receipt); - player.p.dt.mas_expire=future_s(3*24*60*60); + player.auth_type = "mas"; + player.auth_id = ("" + body.receipt.download_id).substr(0, 4) + body.receipt.original_purchase_date_ms; + player.p.mas_auth_id = player.auth_id; + player.p.mas_hash = quick_hash(receipt); + player.p.dt.mas_expire = future_s(3 * 24 * 60 * 60); delete player.s.authfail; - } - else - { + } else { delete player.temp_auth; - console.log("#M: mas declined: "+player.name+" "+(body&&body.status)); + console.log("#M: mas declined: " + player.name + " " + (body && body.status)); } - }); + }, + ); } } -function verify_steam_ownership(player) -{ - var url="https://partner.steam-api.com/ISteamUser/CheckAppOwnership/v2/"; - data={ - key:variables.steam_partner_key, - steamid:player.p.steam_id, - appid:"777150", - } - request.get({url:url,qs:data},function(err,response,body){ +function verify_steam_ownership(player) { + var url = "https://partner.steam-api.com/ISteamUser/CheckAppOwnership/v2/"; + data = { + key: variables.steam_partner_key, + steamid: player.p.steam_id, + appid: "777150", + }; + request.get({ url: url, qs: data }, function (err, response, body) { console.log(body); }); } -function initiate_steam_microtxn(player) -{ - var url="https://partner.steam-api.com/ISteamMicroTxn/InitTxn/v3/"; - var orderid=parseInt(Math.random()*1000000000+1); +function initiate_steam_microtxn(player) { + var url = "https://partner.steam-api.com/ISteamMicroTxn/InitTxn/v3/"; + var orderid = parseInt(Math.random() * 1000000000 + 1); console.log(orderid); - data={ - key:variables.steam_partner_key, - steamid:player.p.steam_id, - appid:"777150", - usersession:"web", - ipaddress:"85.98.170.74", - orderid:orderid, - itemcount:1, - language:"en", - currency:"USD", - "itemid[0]":"123", - "qty[0]":1, - "amount[0]":999, // $9.99 - "description[0]":"1000 Shells", - } - request.post({url:url,form:data},function(err,response,body){ + data = { + key: variables.steam_partner_key, + steamid: player.p.steam_id, + appid: "777150", + usersession: "web", + ipaddress: "85.98.170.74", + orderid: orderid, + itemcount: 1, + language: "en", + currency: "USD", + "itemid[0]": "123", + "qty[0]": 1, + "amount[0]": 999, // $9.99 + "description[0]": "1000 Shells", + }; + request.post({ url: url, form: data }, function (err, response, body) { console.log(body); }); } -function invincible_logic(player,place) -{ - if(is_pvp || G.maps[player.map].pvp) - { - player.s.invincible={ms:max(6000,player.s.invincible&&player.s.invincible.ms||0)}; +function invincible_logic(player, place) { + if (is_pvp || G.maps[player.map].pvp) { + player.s.invincible = { ms: max(6000, (player.s.invincible && player.s.invincible.ms) || 0) }; } } -function serverhop_logic(player) -{ +function serverhop_logic(player) { delete player.s.hopsickness; - player.p.entries=player.p.entries||[]; - var servers={},main=null,mx=0,hops=0; - for(var i=player.p.entries.length-2;i>=0;i--) - { - var hours = Math.abs(new Date(player.p.entries[i+1][1])-new Date(player.p.entries[i][1])) / 36e5; - servers[player.p.entries[i][0]]=(servers[player.p.entries[i][0]]||0)+hours; + player.p.entries = player.p.entries || []; + var servers = {}; + var main = null; + var mx = 0; + var hops = 0; + for (var i = player.p.entries.length - 2; i >= 0; i--) { + var hours = Math.abs(new Date(player.p.entries[i + 1][1]) - new Date(player.p.entries[i][1])) / 36e5; + servers[player.p.entries[i][0]] = (servers[player.p.entries[i][0]] || 0) + hours; } - for(var id in servers) - { - if(servers[id]>mx) main=id,mx=servers[id]; + for (var id in servers) { + if (servers[id] > mx) { + main = id; + mx = servers[id]; + } } - for(var i=player.p.entries.length-1;i>=0;i--) - { - if(hsince(new Date(player.p.entries[i][1]))<4 && player.p.entries[i][0]!=main) hops++; + for (var i = player.p.entries.length - 1; i >= 0; i--) { + if (hsince(new Date(player.p.entries[i][1])) < 4 && player.p.entries[i][0] != main) { + hops++; + } } - if(main && hops && main!=region+server_name && player.level>=60) - { - player.s.hopsickness={ms:min(10000*hops*hops,3*60*60*1000)}; - if(hops>20) player.s.hopsickness.luck=-80,player.s.hopsickness.gold=-80,player.s.hopsickness.output=-50; - else if(hops>15) player.s.hopsickness.luck=-60,player.s.hopsickness.gold=-60,player.s.hopsickness.output=-40; - else if(hops>10) player.s.hopsickness.luck=-40,player.s.hopsickness.gold=-40,player.s.hopsickness.output=-30; - else if(hops>5) player.s.hopsickness.luck=-30,player.s.hopsickness.gold=-30,player.s.hopsickness.output=-20; - else if(hops>2) player.s.hopsickness.luck=-20,player.s.hopsickness.gold=-20,player.s.hopsickness.output=-10; + if (main && hops && main != region + server_name && player.level >= 60) { + player.s.hopsickness = { ms: min(10000 * hops * hops, 3 * 60 * 60 * 1000) }; + if (hops > 20) { + player.s.hopsickness.luck = -80; + player.s.hopsickness.gold = -80; + player.s.hopsickness.output = -50; + } else if (hops > 15) { + player.s.hopsickness.luck = -60; + player.s.hopsickness.gold = -60; + player.s.hopsickness.output = -40; + } else if (hops > 10) { + player.s.hopsickness.luck = -40; + player.s.hopsickness.gold = -40; + player.s.hopsickness.output = -30; + } else if (hops > 5) { + player.s.hopsickness.luck = -30; + player.s.hopsickness.gold = -30; + player.s.hopsickness.output = -20; + } else if (hops > 2) { + player.s.hopsickness.luck = -20; + player.s.hopsickness.gold = -20; + player.s.hopsickness.output = -10; + } calculate_player_stats(player); } - var c=[region+server_name,""+(new Date())]; + var c = [region + server_name, "" + new Date()]; player.p.entries.push(c); - if(player.p.entries.length>60) player.p.entries.shift(); + if (player.p.entries.length > 60) { + player.p.entries.shift(); + } } -function serverhop_logic(player) -{ - if(player.p.entries && player.p.entries[0] && player.p.entries[0][0]==region+server_name) return; +function serverhop_logic(player) { + if (player.p.entries && player.p.entries[0] && player.p.entries[0][0] == region + server_name) { + return; + } delete player.s.hopsickness; - if(!player.p.home) player.p.home=region+server_name; - player.p.entries=[[region+server_name,""+(new Date())]]; - if(player.p.home!=region+server_name && player.level>=60 && server_name!="PVP") - add_condition(player,"hopsickness"); + if (!player.p.home) { + player.p.home = region + server_name; + } + player.p.entries = [[region + server_name, "" + new Date()]]; + if (player.p.home != region + server_name && player.level >= 60 && server_name != "PVP") { + add_condition(player, "hopsickness"); + } } -function ghash(entity,zone,a_d,b_d) //zone is the square's dimension, a_d, and b_d are displacements -{ - var a=floor(1.0*entity.x/zone)+(a_d||0),b=floor(1.0*entity.y/zone)+(b_d||0); - return a+"|"+b; +function ghash(entity, zone, a_d, b_d) { + //zone is the square's dimension, a_d, and b_d are displacements + var a = floor((1.0 * entity.x) / zone) + (a_d || 0); + var b = floor((1.0 * entity.y) / zone) + (b_d || 0); + return a + "|" + b; } -function set_ghash(hash,entity,zone) -{ - var h=ghash(entity,zone); - if(!hash[h]) hash[h]={}; - hash[h][entity.id]=entity; +function set_ghash(hash, entity, zone) { + var h = ghash(entity, zone); + if (!hash[h]) { + hash[h] = {}; + } + hash[h][entity.id] = entity; } -function get_nearby_ghash(hash,entity,zone) -{ - var d=[-1,0,1],l=[],h; - for(var i=0;i<3;i++) - for(var j=0;j<3;j++) - { - h=ghash(entity,zone,d[i],d[j]); - for(var id in hash[h]) l.push(hash[h][id]); +function get_nearby_ghash(hash, entity, zone) { + var d = [-1, 0, 1]; + var l = []; + var h; + for (var i = 0; i < 3; i++) { + for (var j = 0; j < 3; j++) { + h = ghash(entity, zone, d[i], d[j]); + for (var id in hash[h]) { + l.push(hash[h][id]); + } } + } return l; } -function screenshot_npc(player,args) -{ - if(!args) args={}; - if(!args.name) args.name=randomStr(10); - instance=instances[player.in]; - var npc=create_npc({name:args.name,level:args.level||40,speed:1200,hp:100000000,skin:args.skin||"mranger",cx:args.cx||[]},{position:[player.x,player.y],id:args.name},instance); - if(args.stand) npc.p.stand=args.stand; - npc.screenshot=true; - npc.direction=player.direction; - npc.id=args.name; - instance.players[NPC_prefix+npc.id]=npc; +function screenshot_npc(player, args) { + if (!args) { + args = {}; + } + if (!args.name) { + args.name = randomStr(10); + } + instance = instances[player.in]; + var npc = create_npc( + { + name: args.name, + level: args.level || 40, + speed: 1200, + hp: 100000000, + skin: args.skin || "mranger", + cx: args.cx || [], + }, + { position: [player.x, player.y], id: args.name }, + instance, + ); + if (args.stand) { + npc.p.stand = args.stand; + } + npc.screenshot = true; + npc.direction = player.direction; + npc.id = args.name; + instance.players[NPC_prefix + npc.id] = npc; } -function get_npc_coords(id) -{ - for(var iid in instances) - { - if(G.maps[iid] && G.maps[iid].ref && G.maps[iid].ref[id]) +function get_npc_coords(id) { + for (var iid in instances) { + if (G.maps[iid] && G.maps[iid].ref && G.maps[iid].ref[id]) { return G.maps[iid].ref[id]; + } } - return {x:0,y:0,map:"main",in:"main"}; + return { x: 0, y: 0, map: "main", in: "main" }; } -function pmap_remove(player) -{ - try{ +function pmap_remove(player) { + try { delete instances[player.in].pmap[player.last_hash][player.id]; - if(!Object.keys(instances[player.in].pmap[player.last_hash]).length) delete instances[player.in].pmap[player.last_hash]; - } - catch(e){} + if (!Object.keys(instances[player.in].pmap[player.last_hash]).length) { + delete instances[player.in].pmap[player.last_hash]; + } + } catch (e) {} } -function pmap_get(player) -{ - var hash=ghash(player,6); - if(!instances[player.in].pmap[hash]) return {}; +function pmap_get(player) { + var hash = ghash(player, 6); + if (!instances[player.in].pmap[hash]) { + return {}; + } return instances[player.in].pmap[hash]; } -function pmap_add(player) -{ - var hash=ghash(player,6); - if(!instances[player.in].pmap[hash]) instances[player.in].pmap[hash]={}; - instances[player.in].pmap[hash][player.id]=player; - player.last_hash=hash; +function pmap_add(player) { + var hash = ghash(player, 6); + if (!instances[player.in].pmap[hash]) { + instances[player.in].pmap[hash] = {}; + } + instances[player.in].pmap[hash][player.id] = player; + player.last_hash = hash; } -function pmap_move(player) -{ - var hash=ghash(player,6); - if(player.last_hash==hash) return; +function pmap_move(player) { + var hash = ghash(player, 6); + if (player.last_hash == hash) { + return; + } pmap_remove(player); pmap_add(player); } -function init_tavern() -{ - if(!instances.tavern) return; - tavern=instances.tavern; - tavern.roulette={ - state:"bets", - next:future_s(25), - "odds":{ - "red":2, - "black":2, - "odd":2, - "even":2, - "1/3":3, - "2/3":3, - "3/3":3, - "1/2":2, - "2/2":2, - "3n":3, - "3n+1":3, - "3n+2":3, +function init_tavern() { + if (!instances.tavern) { + return; + } + tavern = instances.tavern; + tavern.roulette = { + state: "bets", + next: future_s(25), + odds: { + red: 2, + black: 2, + odd: 2, + even: 2, + "1/3": 3, + "2/3": 3, + "3/3": 3, + "1/2": 2, + "2/2": 2, + "3n": 3, + "3n+1": 3, + "3n+2": 3, }, - gain:0, - players:{}, + gain: 0, + players: {}, }; - for(var i=0;i<=36;i++) tavern.roulette.odds[""+i]=36; - tavern.poker={ - rooms:[], - gain:0, + for (var i = 0; i <= 36; i++) { + tavern.roulette.odds["" + i] = 36; + } + tavern.poker = { + rooms: [], + gain: 0, }; - tavern.dice={ - rolls:[], - state:"lock", - seconds:0, - next:future_s(1), - players:{}, - bets:[], + tavern.dice = { + rolls: [], + state: "lock", + seconds: 0, + next: future_s(1), + players: {}, + bets: [], }; - tavern.dice.num=""+parseInt(Math.random()*10)+""+parseInt(Math.random()*10)+"."+parseInt(Math.random()*10)+""+parseInt(Math.random()*10); - tavern.info.dice="lock"; - tavern.info.num=tavern.dice.num; - tavern.info.seconds=tavern.dice.seconds; + tavern.dice.num = + "" + + parseInt(Math.random() * 10) + + "" + + parseInt(Math.random() * 10) + + "." + + parseInt(Math.random() * 10) + + "" + + parseInt(Math.random() * 10); + tavern.info.dice = "lock"; + tavern.info.num = tavern.dice.num; + tavern.info.seconds = tavern.dice.seconds; } -function house_debt() -{ - var gold=0; - for(var id in tavern.dice.players) - { - var player=players[id]; if(!player) continue; - for(var b_id in player.bets||{}) - { - var bet=player.bets[b_id]; if(bet.type!="dice") continue; - gold+=bet.win-bet.edge-bet.gold; +function house_debt() { + var gold = 0; + for (var id in tavern.dice.players) { + var player = players[id]; + if (!player) { + continue; + } + for (var b_id in player.bets || {}) { + var bet = player.bets[b_id]; + if (bet.type != "dice") { + continue; + } + gold += bet.win - bet.edge - bet.gold; } } return gold; } -function house_edge() -{ - var gold=S.gold-house_debt(); - if(gold>5000000000) return 0.5; - if(gold>2000000000) return 1; - if(gold>1000000000) return 1.5; +function house_edge() { + var gold = S.gold - house_debt(); + if (gold > 5000000000) { + return 0.5; + } + if (gold > 2000000000) { + return 1; + } + if (gold > 1000000000) { + return 1.5; + } return 2; } -var dice_last_roll=0; -function tavern_loop() -{ try{ - if(!server.live || !instances.tavern) return; - var c=new Date(); - if(c>tavern.dice.next) - { - if(tavern.dice.state=="bets") - { - tavern.dice.seconds+=1; tavern.info.seconds=tavern.dice.seconds; - if(tavern.dice.seconds>=30) - { - tavern.info.dice=tavern.dice.state="roll"; - dice_last_roll=tavern.info.num; - delete tavern.info.num; - tavern.dice.next=future_s(10); - instance_emit(tavern,"dice",{state:"roll"}); - //server_log("Dice: Bets Over"); - } +var dice_last_roll = 0; +function tavern_loop() { + try { + if (!server.live || !instances.tavern) { + return; } - else if(tavern.dice.state=="roll") - { - tavern.info.dice=tavern.dice.state="lock"; - tavern.info.num=tavern.dice.num; - tavern.dice.next=future_s(1.6); - for(var id in tavern.dice.players) - { - var player=players[id]; if(!player) continue; - for(var b_id in player.bets||{}) - { - var bet=player.bets[b_id]; if(bet.type!="dice") continue; - delete players[bet.pid].bets[bet.id]; - tavern.dice.bets.push(bet); - if(bet.dir=="up" && parseFloat(tavern.dice.num)>=bet.num || bet.dir=="down" && parseFloat(tavern.dice.num)<=bet.num) - { - if(player.type=="merchant") player.xp+=parseInt(bet.edge*7.2); - player.gold+=bet.win-bet.edge,S.gold-=bet.win-bet.edge-bet.gold; - if(bet.win-bet.edge-bet.gold>=12000000) lstack(S.logs.dice,{name:player.name,gold:bet.win-bet.edge-bet.gold,odds:bet.odds}); + var c = new Date(); + if (c > tavern.dice.next) { + if (tavern.dice.state == "bets") { + tavern.dice.seconds += 1; + tavern.info.seconds = tavern.dice.seconds; + if (tavern.dice.seconds >= 30) { + tavern.info.dice = tavern.dice.state = "roll"; + dice_last_roll = tavern.info.num; + delete tavern.info.num; + tavern.dice.next = future_s(10); + instance_emit(tavern, "dice", { state: "roll" }); + //server_log("Dice: Bets Over"); + } + } else if (tavern.dice.state == "roll") { + tavern.info.dice = tavern.dice.state = "lock"; + tavern.info.num = tavern.dice.num; + tavern.dice.next = future_s(1.6); + for (var id in tavern.dice.players) { + var player = players[id]; + if (!player) { + continue; } - else - { - if(bet.gold>=10000000) lstack(S.logs.dice,{name:player.name,gold:-bet.gold,odds:bet.odds}); - S.gold+=bet.gold; - } - } - } - instance_emit(tavern,"dice",{state:"lock",num:tavern.dice.num,text:tavern.dice.text,key:tavern.dice.key}); - //server_log("Dice: Roll Over - Locking"); - } - else if(tavern.dice.state=="lock") - { - tavern.dice.state="suspense"; - tavern.dice.bets.forEach(function(bet){ - var player=players[bet.pid]; if(!player) return; - if(bet.dir=="up" && parseFloat(tavern.dice.num)>=bet.num || bet.dir=="down" && parseFloat(tavern.dice.num)<=bet.num) - { - player.socket.emit("game_log",{"message":"Won: "+to_pretty_num(bet.win)+" gold ["+tavern.dice.num+"]","color":"gold"}); - player.socket.emit("game_log",{"message":"House edge: "+to_pretty_num(bet.edge)+" gold","color":"gray"}); - instance_emit(tavern,"tavern",{event:"won",name:player.name,type:"dice",num:bet.num,gold:bet.win,dir:bet.dir,net:bet.win-bet.gold-bet.edge}); - } - else - { - player.socket.emit("game_log",{"message":"Lost: "+to_pretty_num(bet.gold)+" gold ["+tavern.dice.num+"]","color":"gray"}); - instance_emit(tavern,"tavern",{event:"lost",name:player.name,type:"dice",num:bet.num,gold:bet.gold,dir:bet.dir}); - } - if(player.xp>=player.max_xp) resend(player,"reopen"); - else resend(player,"reopen+nc"); - }); - tavern.dice.next=future_s(2); - //server_log("Dice: Locked - Suspensing"); - } - else if(tavern.dice.state=="suspense") - { - var timer=new Date(); - tavern.info.dice=tavern.dice.state="bets"; - tavern.info.seconds=tavern.dice.seconds=0; - tavern.dice.players={}; tavern.dice.bets=[]; - tavern.dice.num=""+parseInt(Math.random()*10)+""+parseInt(Math.random()*10)+"."+parseInt(Math.random()*10)+""+parseInt(Math.random()*10); - tavern.dice.key=randomStr(20+parseInt(Math.random()*20)); - var initials=""; for(var id in tavern.players) if(!tavern.players[id].npc) initials+=tavern.players[id].name[0]; - tavern.dice.text="Num: "+tavern.dice.num+" Initials: "+initials+" Random: "+randomStr(16+parseInt(Math.random()*16)); - var hmac=crypto.createHmac('sha256',tavern.dice.key); - hmac.update(tavern.dice.text); - tavern.dice.hex=hmac.digest('hex'); - hmac.end(); - instance_emit(tavern,"dice",{state:"bets",hex:tavern.dice.hex,algorithm:"hmac-sha256"}); - if(gameplay=="hardcore" && Math.random()<0.07 && dice_last_roll) tavern.dice.num=dice_last_roll; - //server_log("Dice: Bets Starting For: "+tavern.dice.num+" in "+mssince(timer)+"ms"); - } - } - if(c>tavern.roulette.next && 0) - { - if(tavern.roulette.state=="bets") - { - server_log("Roulette: Bets Over"); - tavern.roulette.state="roll"; - tavern.roulette.next=future_s(10); - } - else if(tavern.roulette.state=="roll") - { - tavern.roulette.roll=floor(Math.random()*37); - tavern.roulette.state="award"; - tavern.roulette.next=future_s(5); - server_log("Roulette: Rolled "+tavern.roulette.roll); - } - else if(tavern.roulette.state=="award") - { - var winners={},roll=tavern.roulette.roll; - if(roll==0) winners["0"]=1; - else - { - if(roll%2) winners["odd"]=1; - else winners["even"]=1; - - if([2,4,6,8,10,11,13,15,17,20,22,24,26,28,29,31,33,35].indexOf(roll)!=-1) winners["black"]=1; - else winners["red"]=1; - - if(roll<=12) winners["1/3"]=1; - else if(roll<=24) winners["2/3"]=1; - else winners["3/3"]=1; - - if(roll<=18) winners["1/2"]=1; - else winners["2/2"]=1; - - if((roll%3)==1) winners["3n+1"]=1; - else if((roll%3)==2) winners["3n+2"]=1; - else winners["3n"]=1; - } - var totals={}; - try{ - for(var id in tavern.roulette.players) - { - var player=players[id]; if(!player) continue; - for(var b_id in player.bets||{}) - { - var bet=player.bets[b_id]; if(bet.type!="roulette") continue; - if(!totals[bet.pid]) totals[bet.pid]=0; - if(winners[bet.odd]) totals[bet.pid]+=bet.gold*tavern.roulette.odds[bet.odd],tavern.roulette.gain-=bet.gold*tavern.roulette.odds[bet.odd]; - else tavern.roulette.gain+=bet.gold; - // else totals[bet.pid]-=bet.gold; - if(players[bet.pid]) - { - delete players[bet.pid].bets[bet.id]; - if(winners[bet.odd]) players[bet.pid].gold+=bet.gold*tavern.roulette.odds[bet.odd]; + for (var b_id in player.bets || {}) { + var bet = player.bets[b_id]; + if (bet.type != "dice") { + continue; + } + delete players[bet.pid].bets[bet.id]; + tavern.dice.bets.push(bet); + if ( + (bet.dir == "up" && parseFloat(tavern.dice.num) >= bet.num) || + (bet.dir == "down" && parseFloat(tavern.dice.num) <= bet.num) + ) { + if (player.type == "merchant") { + player.xp += parseInt(bet.edge * 7.2); + } + player.gold += bet.win - bet.edge; + S.gold -= bet.win - bet.edge - bet.gold; + if (bet.win - bet.edge - bet.gold >= 12000000) { + lstack(S.logs.dice, { name: player.name, gold: bet.win - bet.edge - bet.gold, odds: bet.odds }); + } + } else { + if (bet.gold >= 10000000) { + lstack(S.logs.dice, { name: player.name, gold: -bet.gold, odds: bet.odds }); + } + S.gold += bet.gold; } } } - }catch(e){ log_trace("Critical-roulette_win",e); } - server_log("Roulette: Awards | Gain: "+to_pretty_num(tavern.roulette.gain)); - for(var id in totals) - { - var player=players[id]; if(!player) continue; - if(winners["0"]) player.socket.emit("game_log",{message:"The lucky number is "+roll+" Green",color:"#2E7C2A"}); - else if(winners["red"]) player.socket.emit("game_log",{message:"The lucky number is "+roll+" Red",color:"#911609"}); - else player.socket.emit("game_log",{message:"The lucky number is "+roll+" Black",color:"#5C5D5D"}); - if(!totals[id]) - { - player.socket.emit("game_log","Better luck next time"); + instance_emit(tavern, "dice", { + state: "lock", + num: tavern.dice.num, + text: tavern.dice.text, + key: tavern.dice.key, + }); + //server_log("Dice: Roll Over - Locking"); + } else if (tavern.dice.state == "lock") { + tavern.dice.state = "suspense"; + tavern.dice.bets.forEach(function (bet) { + var player = players[bet.pid]; + if (!player) { + return; + } + if ( + (bet.dir == "up" && parseFloat(tavern.dice.num) >= bet.num) || + (bet.dir == "down" && parseFloat(tavern.dice.num) <= bet.num) + ) { + player.socket.emit("game_log", { + message: "Won: " + to_pretty_num(bet.win) + " gold [" + tavern.dice.num + "]", + color: "gold", + }); + player.socket.emit("game_log", { + message: "House edge: " + to_pretty_num(bet.edge) + " gold", + color: "gray", + }); + instance_emit(tavern, "tavern", { + event: "won", + name: player.name, + type: "dice", + num: bet.num, + gold: bet.win, + dir: bet.dir, + net: bet.win - bet.gold - bet.edge, + }); + } else { + player.socket.emit("game_log", { + message: "Lost: " + to_pretty_num(bet.gold) + " gold [" + tavern.dice.num + "]", + color: "gray", + }); + instance_emit(tavern, "tavern", { + event: "lost", + name: player.name, + type: "dice", + num: bet.num, + gold: bet.gold, + dir: bet.dir, + }); + } + if (player.xp >= player.max_xp) { + resend(player, "reopen"); + } else { + resend(player, "reopen+nc"); + } + }); + tavern.dice.next = future_s(2); + //server_log("Dice: Locked - Suspensing"); + } else if (tavern.dice.state == "suspense") { + var timer = new Date(); + tavern.info.dice = tavern.dice.state = "bets"; + tavern.info.seconds = tavern.dice.seconds = 0; + tavern.dice.players = {}; + tavern.dice.bets = []; + tavern.dice.num = + "" + + parseInt(Math.random() * 10) + + "" + + parseInt(Math.random() * 10) + + "." + + parseInt(Math.random() * 10) + + "" + + parseInt(Math.random() * 10); + tavern.dice.key = randomStr(20 + parseInt(Math.random() * 20)); + var initials = ""; + for (var id in tavern.players) { + if (!tavern.players[id].npc) { + initials += tavern.players[id].name[0]; + } } - else - { - player.socket.emit("game_log",{message:"You've won "+to_pretty_num(totals[id])+" gold",color:"gold"}); + tavern.dice.text = + "Num: " + + tavern.dice.num + + " Initials: " + + initials + + " Random: " + + randomStr(16 + parseInt(Math.random() * 16)); + var hmac = crypto.createHmac("sha256", tavern.dice.key); + hmac.update(tavern.dice.text); + tavern.dice.hex = hmac.digest("hex"); + hmac.end(); + instance_emit(tavern, "dice", { state: "bets", hex: tavern.dice.hex, algorithm: "hmac-sha256" }); + if (gameplay == "hardcore" && Math.random() < 0.07 && dice_last_roll) { + tavern.dice.num = dice_last_roll; } - resend(player,"reopen+nc"); + //server_log("Dice: Bets Starting For: "+tavern.dice.num+" in "+mssince(timer)+"ms"); } - tavern.roulette.state="bets"; - tavern.roulette.next=future_s(25); - server_log("Roulette: Bets Open"); + } + if (c > tavern.roulette.next && 0) { + if (tavern.roulette.state == "bets") { + server_log("Roulette: Bets Over"); + tavern.roulette.state = "roll"; + tavern.roulette.next = future_s(10); + } else if (tavern.roulette.state == "roll") { + tavern.roulette.roll = floor(Math.random() * 37); + tavern.roulette.state = "award"; + tavern.roulette.next = future_s(5); + server_log("Roulette: Rolled " + tavern.roulette.roll); + } else if (tavern.roulette.state == "award") { + var winners = {}; + var roll = tavern.roulette.roll; + if (roll == 0) { + winners["0"] = 1; + } else { + if (roll % 2) { + winners["odd"] = 1; + } else { + winners["even"] = 1; + } + + if ([2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35].indexOf(roll) != -1) { + winners["black"] = 1; + } else { + winners["red"] = 1; + } + + if (roll <= 12) { + winners["1/3"] = 1; + } else if (roll <= 24) { + winners["2/3"] = 1; + } else { + winners["3/3"] = 1; + } + + if (roll <= 18) { + winners["1/2"] = 1; + } else { + winners["2/2"] = 1; + } + + if (roll % 3 == 1) { + winners["3n+1"] = 1; + } else if (roll % 3 == 2) { + winners["3n+2"] = 1; + } else { + winners["3n"] = 1; + } + } + var totals = {}; + try { + for (var id in tavern.roulette.players) { + var player = players[id]; + if (!player) { + continue; + } + for (var b_id in player.bets || {}) { + var bet = player.bets[b_id]; + if (bet.type != "roulette") { + continue; + } + if (!totals[bet.pid]) { + totals[bet.pid] = 0; + } + if (winners[bet.odd]) { + totals[bet.pid] += bet.gold * tavern.roulette.odds[bet.odd]; + tavern.roulette.gain -= bet.gold * tavern.roulette.odds[bet.odd]; + } else { + tavern.roulette.gain += bet.gold; + } + // else totals[bet.pid]-=bet.gold; + if (players[bet.pid]) { + delete players[bet.pid].bets[bet.id]; + if (winners[bet.odd]) { + players[bet.pid].gold += bet.gold * tavern.roulette.odds[bet.odd]; + } + } + } + } + } catch (e) { + log_trace("Critical-roulette_win", e); + } + server_log("Roulette: Awards | Gain: " + to_pretty_num(tavern.roulette.gain)); + for (var id in totals) { + var player = players[id]; + if (!player) { + continue; + } + if (winners["0"]) { + player.socket.emit("game_log", { message: "The lucky number is " + roll + " Green", color: "#2E7C2A" }); + } else if (winners["red"]) { + player.socket.emit("game_log", { message: "The lucky number is " + roll + " Red", color: "#911609" }); + } else { + player.socket.emit("game_log", { message: "The lucky number is " + roll + " Black", color: "#5C5D5D" }); + } + if (!totals[id]) { + player.socket.emit("game_log", "Better luck next time"); + } else { + player.socket.emit("game_log", { + message: "You've won " + to_pretty_num(totals[id]) + " gold", + color: "gold", + }); + } + resend(player, "reopen+nc"); + } + tavern.roulette.state = "bets"; + tavern.roulette.next = future_s(25); + server_log("Roulette: Bets Open"); + } + } + tavern.poker.rooms.forEach(function (room) { + if (room.state == "shuffle") { + room.cards = []; + for (var i = 1; i <= 52; i++) { + room.cards.push(i); + } + shuffle(room.cards); + } + }); + } catch (e) { + log_trace("Critical-tavern_loop", e); + } +} + +function create_npc(npc, map_def, instance) { + var entity = { + speed: npc.speed || 20, + attack: npc.attack || 100, + range: npc.range || 40, + level: npc.level || 100, + hp: npc.hp || 1200, + max_hp: npc.hp || 1200, + armor: 500, + mp: 2000, + a: {}, + xp: 0, + pdps: 0, // once got killed by a blaster ... [07/04/21] + skin: npc.skin, + name: npc.name, + red_zone: 0, + in: instance.name, + map: instance.map, + def: npc, + npc: map_def.id, + ntype: map_def.id, + is_player: true, + is_npc: true, + type: npc["class"] || "merchant", + id: "$" + npc.name, + gold: 0, + items: [], + citems: [], + cid: 0, + u: true, + citizen: true, + luckm: 1, // for loot_all_monsters + s: {}, + c: {}, + q: {}, + p: {}, + m: 0, + slots: {}, + bets: {}, + vision: [0, 0], + socket: false_socket, + cx: npc.cx || {}, + base: { h: 8, v: 7, vn: 2 }, + last: { move: new Date(), attack: really_old, attacked: really_old }, + delay: npc.delay || 600, + d_multiplier: 1, + steps: npc.steps || 40, + }; + + if (npc.slots) { + entity.slots = npc.slots; + } + if (npc.projectile) { + entity.projectile = npc.projectile; + } + + if (map_def.positions) { + entity.x = map_def.positions[0][0]; + entity.y = map_def.positions[0][1]; + entity.positions = map_def.positions; + } else { + entity.x = map_def.position[0]; + entity.y = map_def.position[1]; + if (npc.type == "fullstatic" && map_def.position.length == 3) { + entity.direction = map_def.position[2]; } } - tavern.poker.rooms.forEach(function(room){ - if(room.state=="shuffle") - { - room.cards=[]; - for(var i=1;i<=52;i++) room.cards.push(i); - shuffle(room.cards); - } - }); -}catch(e){ log_trace("Critical-tavern_loop",e);} } - -function create_npc(npc,map_def,instance) -{ - var entity={ - speed:npc.speed||20, - attack:npc.attack||100, - range:npc.range||40, - level:npc.level||100,hp:npc.hp||1200,max_hp:npc.hp||1200,armor:500,mp:2000, - a:{}, - xp:0, pdps:0, // once got killed by a blaster ... [07/04/21] - skin:npc.skin,name:npc.name, - red_zone:0, - in:instance.name,map:instance.map, - def:npc, - npc:map_def.id, - ntype:map_def.id,is_player:true,is_npc:true, - type:npc['class']||"merchant", - id:"$"+npc.name, - gold:0,items:[],citems:[], - cid:0,u:true, - citizen:true, - luckm:1, // for loot_all_monsters - s:{},c:{},q:{},p:{},m:0,slots:{},bets:{}, - vision:[0,0],socket:false_socket,cx:npc.cx||{}, - base:{h:8,v:7,vn:2}, - last:{move:new Date(),attack:really_old,attacked:really_old}, - delay:npc.delay||600,d_multiplier:1,steps:npc.steps||40, + if (map_def.boundary) { + entity.boundary = map_def.boundary; + } + if (map_def.loop) { + entity.going_x = entity.x; + entity.going_y = entity.y; + start_moving_element(entity); + entity.loop = true; + entity.last_m = 0; } - if(npc.slots) entity.slots=npc.slots; - if(npc.projectile) entity.projectile=npc.projectile; - - if(map_def.positions) entity.x=map_def.positions[0][0],entity.y=map_def.positions[0][1],entity.positions=map_def.positions; - else - { - entity.x=map_def.position[0],entity.y=map_def.position[1]; - if(npc.type=="fullstatic" && map_def.position.length==3) entity.direction=map_def.position[2]; + if (npc.role == "citizen" || npc.moving) { + entity.movable = true; } - - if(map_def.boundary) entity.boundary=map_def.boundary; - if(map_def.loop) - { - entity.going_x=entity.x,entity.going_y=entity.y; start_moving_element(entity); - entity.loop=true,entity.last_m=0; - } - - if(npc.role=="citizen" || npc.moving) - entity.movable=true; return entity; } -function create_instance(name,map_name,args) -{ - if(!map_name) map_name=name; - if(!smap_data[map_name] && smap_data[map_name]!=-1) server_bfs(map_name); - if(!args) args={}; - var instance={players:{},monsters:{},observers:{},map:map_name,allow:true,name:name,pmap:{},rage_list:[],last_update:future_ms(parseInt(Math.random()*30)),last_player:future_s(240),npcs:0,paused:false,info:{},operators:1}; - instances[name]=instance; - if(args.solo) instance.solo=args.solo; - if(args.pvp) instance.pvp=true,instance.allow=false; - if(args.event) instance.allow=false; - var map=G.maps[map_name]; - if(map.mount && gameplay=="normal") instance.mount=true; - for(var i=0;i<(map.monsters||[]).length;i++) - { - var map_def=map.monsters[i]; - if(map_def.special) continue; - if(map_def.rage) - { - map_def.id=randomStr(5); +function create_instance(name, map_name, args) { + if (!map_name) { + map_name = name; + } + if (!smap_data[map_name] && smap_data[map_name] != -1) { + server_bfs(map_name); + } + if (!args) { + args = {}; + } + var instance = { + players: {}, + monsters: {}, + observers: {}, + map: map_name, + allow: true, + name: name, + pmap: {}, + rage_list: [], + last_update: future_ms(parseInt(Math.random() * 30)), + last_player: future_s(240), + npcs: 0, + paused: false, + info: {}, + operators: 1, + }; + instances[name] = instance; + if (args.solo) { + instance.solo = args.solo; + } + if (args.pvp) { + instance.pvp = true; + instance.allow = false; + } + if (args.event) { + instance.allow = false; + } + var map = G.maps[map_name]; + if (map.mount && gameplay == "normal") { + instance.mount = true; + } + for (var i = 0; i < (map.monsters || []).length; i++) { + var map_def = map.monsters[i]; + if (map_def.special) { + continue; + } + if (map_def.rage) { + map_def.id = randomStr(5); instance.rage_list.push(map_def); } - for(var j=0;j=12 is a must."); + map.spawns.forEach(function (spawn) { + var cdist = closest_line(map_name, spawn[0], spawn[1]); + if (cdist < 12) { + console.log( + "Spawn [" + map_name + "," + spawn[0] + "," + spawn[1] + "] is " + cdist + " close to a line! >=12 is a must.", + ); + } }); - server_log("Created an instance of "+instances[name].map,1); + server_log("Created an instance of " + instances[name].map, 1); return instance; } -function pause_instance(instance) -{ - server_log("Paused: "+instance.name); - instance.paused=true; +function pause_instance(instance) { + server_log("Paused: " + instance.name); + instance.paused = true; } -function resume_instance(instance) -{ - if(!instance.paused) return; +function resume_instance(instance) { + if (!instance.paused) { + return; + } // server_log("Resumed: "+instance.name); - instance.paused=false; + instance.paused = false; update_instance(instance); } -function destroy_instance(name) -{ - for(var id in instances[name].players) - { - var player=instances[name].players[id]; - if(player.npc) continue; - if(player.state && !player.state.restored && instances[player.state.map] && !instances[player.state.map].mount && player.in!=name) - transport_player_to(player,player.state.map,[player.state.x,player.state.y]); - else transport_player_to(player,"main"); +function destroy_instance(name) { + for (var id in instances[name].players) { + var player = instances[name].players[id]; + if (player.npc) { + continue; + } + if ( + player.state && + !player.state.restored && + instances[player.state.map] && + !instances[player.state.map].mount && + player.in != name + ) { + transport_player_to(player, player.state.map, [player.state.x, player.state.y]); + } else { + transport_player_to(player, "main"); + } restore_state(player); - resend(player,"cid+reopen"); // reopen for abtesting + resend(player, "cid+reopen"); // reopen for abtesting } - for(var id in instances[name].monsters) - { - remove_monster(instances[name].monsters[id],{silent:true}); // to make sure targets are always updated + for (var id in instances[name].monsters) { + remove_monster(instances[name].monsters[id], { silent: true }); // to make sure targets are always updated } - server_log("Deleted an instance of "+instances[name].map,1); + server_log("Deleted an instance of " + instances[name].map, 1); delete instances[name]; } -function spawn_special_monster(type) -{ - if(type=="pinkgoo") - { - var packs=[]; - ["main","cave","halloween","winterland"].forEach(function(m){ - G.maps[m].monsters.forEach(function(p){ - if(!p.boundaries) packs.push({"type":"pinkgoo",boundary:p.boundary,count:1,i:m}); // comment boundary out next year [09/02/19] +function spawn_special_monster(type) { + if (type == "pinkgoo") { + var packs = []; + ["main", "cave", "halloween", "winterland"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "pinkgoo", boundary: p.boundary, count: 1, i: m }); + } // comment boundary out next year [09/02/19] }); }); - var pack=packs[floor(Math.random()*packs.length)]; - pack.gold=D.monster_gold.pinkgoo; - server_log("Love Goo: "+JSON.stringify(pack)); - new_monster(pack.i,pack); - broadcast("game_event",{name:"pinkgoo",map:pack.i}); + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.pinkgoo; + server_log("Love Goo: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "pinkgoo", map: pack.i }); } - if(type=="crabxx") - { - var pack=null; - ["main"].forEach(function(m){ - G.maps[m].monsters.forEach(function(p){ - if(p.type=="crabx") pack={"type":"crabxx",boundary:p.boundary,count:1,i:m}; + if (type == "crabxx") { + var pack = null; + ["main"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (p.type == "crabx") { + pack = { type: "crabxx", boundary: p.boundary, count: 1, i: m }; + } }); }); - pack.gold=D.monster_gold.crabxx; - server_log("Crab XX: "+JSON.stringify(pack)); - new_monster(pack.i,pack); - broadcast("game_event",{name:"crabxx",map:pack.i}); + pack.gold = D.monster_gold.crabxx; + server_log("Crab XX: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "crabxx", map: pack.i }); } - if(type=="snowman") - { - var packs=[]; - ["winterland"].forEach(function(m){ - G.maps[m].monsters.forEach(function(p){ - if(!p.boundaries) packs.push({"type":"snowman",boundary:p.boundary,count:1,i:m}); + if (type == "snowman") { + var packs = []; + ["winterland"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "snowman", boundary: p.boundary, count: 1, i: m }); + } }); }); - var pack=packs[floor(Math.random()*packs.length)]; - pack.gold=D.monster_gold.snowman; - pack={"type":"snowman",boundary:[682,-967,1482,-779],count:1,i:"winterland"} - server_log("Snowman: "+JSON.stringify(pack)); - new_monster(pack.i,pack); - broadcast("game_event",{name:"snowman",map:pack.i,x:900,y:-800}); + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.snowman; + pack = { type: "snowman", boundary: [682, -967, 1482, -779], count: 1, i: "winterland" }; + server_log("Snowman: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "snowman", map: pack.i, x: 900, y: -800 }); } - if(type=="dragold") - { - var packs=[]; - var pack=packs[floor(Math.random()*packs.length)]; - pack={"type":"dragold",boundary:[1018,-940,1385,-624],count:1,i:"cave"}; - pack.gold=100000; - server_log("Dragold: "+JSON.stringify(pack)); - new_monster(pack.i,pack); - broadcast("game_event",{name:"dragold",map:pack.i,x:900,y:-800}); + if (type == "dragold") { + var packs = []; + var pack = packs[floor(Math.random() * packs.length)]; + pack = { type: "dragold", boundary: [1018, -940, 1385, -624], count: 1, i: "cave" }; + pack.gold = 100000; + server_log("Dragold: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "dragold", map: pack.i, x: 900, y: -800 }); } - if(type=="franky") - { - var map="level2w"; - var pack={"type":"franky",boundary:G.maps[map].monsters[0].boundary,count:1,i:map}; - pack.gold=D.monster_gold.franky; - server_log("Franky: "+JSON.stringify(pack)); - new_monster(pack.i,pack); - broadcast("game_event",{name:"franky",map:pack.i,x:G.maps[map].monsters[0].boundary[0],y:G.maps[map].monsters[0].boundary[1]}); + if (type == "franky") { + var map = "level2w"; + var pack = { type: "franky", boundary: G.maps[map].monsters[0].boundary, count: 1, i: map }; + pack.gold = D.monster_gold.franky; + server_log("Franky: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { + name: "franky", + map: pack.i, + x: G.maps[map].monsters[0].boundary[0], + y: G.maps[map].monsters[0].boundary[1], + }); } - if(type=="icegolem") - { - var map="winterland"; - var pack={"type":"icegolem",boundary:[782.25,395.96,888.71,450.28],count:1,i:map,roam:true}; - pack.gold=D.monster_gold.icegolem; - server_log("Ice Golem: "+JSON.stringify(pack)); - new_monster(pack.i,pack); - broadcast("game_event",{name:"icegolem",map:pack.i,x:pack.boundary[0],y:pack.boundary[1]}); + if (type == "icegolem") { + var map = "winterland"; + var pack = { type: "icegolem", boundary: [782.25, 395.96, 888.71, 450.28], count: 1, i: map, roam: true }; + pack.gold = D.monster_gold.icegolem; + server_log("Ice Golem: " + JSON.stringify(pack)); + new_monster(pack.i, pack); + broadcast("game_event", { name: "icegolem", map: pack.i, x: pack.boundary[0], y: pack.boundary[1] }); } - if(type=="mrpumpkin" || type=="mrgreen" || type=="grinch") - { - var pack={},map="none"; - for(var mname in G.maps) - { - var def=G.maps[mname]; - if(def.ignore) continue; - (def.monsters||[]).forEach(function(p) - { - if(p.type==type) pack=p,map=mname; + if (type == "mrpumpkin" || type == "mrgreen" || type == "grinch") { + var pack = {}; + var map = "none"; + for (var mname in G.maps) { + var def = G.maps[mname]; + if (def.ignore) { + continue; + } + (def.monsters || []).forEach(function (p) { + if (p.type == type) { + pack = p; + map = mname; + } }); } - server_log(G.monsters[type].name+": "+JSON.stringify(pack)); - var monster=new_monster(map,pack); - broadcast("game_event",{name:type,map:map,x:pack.boundary[0],y:pack.boundary[1]}); - if(type=="grinch") monster.extra_gold=12000000; + server_log(G.monsters[type].name + ": " + JSON.stringify(pack)); + var monster = new_monster(map, pack); + broadcast("game_event", { name: type, map: map, x: pack.boundary[0], y: pack.boundary[1] }); + if (type == "grinch") { + monster.extra_gold = 12000000; + } } - if(type=="slenderman") - { - var packs=[]; - var m=random_one(["cave","halloween","spookytown"]) - var p=random_place(m); - var pack={"type":"slenderman",boundary:[p.x,p.y,p.x,p.y],count:1,i:m}; - pack.gold=D.monster_gold.slenderman; + if (type == "slenderman") { + var packs = []; + var m = random_one(["cave", "halloween", "spookytown"]); + var p = random_place(m); + var pack = { type: "slenderman", boundary: [p.x, p.y, p.x, p.y], count: 1, i: m }; + pack.gold = D.monster_gold.slenderman; //server_log("Love Goo: "+JSON.stringify(pack)); - var monster=new_monster(pack.i,pack); - broadcast("game_event",{name:"slenderman",map:pack.i}); + var monster = new_monster(pack.i, pack); + broadcast("game_event", { name: "slenderman", map: pack.i }); } - if(type=="tiger") - { - var packs=[]; - var m=random_one(["cave","main"]) - var p=random_place(m); - var pack={"type":"tiger",boundary:[p.x,p.y,p.x,p.y],count:1,i:m}; - pack.gold=D.monster_gold.tiger; + if (type == "tiger") { + var packs = []; + var m = random_one(["cave", "main"]); + var p = random_place(m); + var pack = { type: "tiger", boundary: [p.x, p.y, p.x, p.y], count: 1, i: m }; + pack.gold = D.monster_gold.tiger; //server_log("Love Goo: "+JSON.stringify(pack)); - var monster=new_monster(pack.i,pack); - broadcast("game_event",{name:"tiger",map:pack.i}); + var monster = new_monster(pack.i, pack); + broadcast("game_event", { name: "tiger", map: pack.i }); } - if(type=="wabbit") - { - var packs=[]; - ["main","cave","halloween","winterland","tunnel","mansion","winter_cave"].forEach(function(m){ - G.maps[m].monsters.forEach(function(p){ - if(!p.boundaries) packs.push({"type":"wabbit",boundary:p.boundary,count:1,i:m,roam:true}); + if (type == "wabbit") { + var packs = []; + ["main", "cave", "halloween", "winterland", "tunnel", "mansion", "winter_cave"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "wabbit", boundary: p.boundary, count: 1, i: m, roam: true }); + } }); }); - var pack=packs[floor(Math.random()*packs.length)]; - pack.gold=D.monster_gold.wabbit; + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.wabbit; //server_log("Love Goo: "+JSON.stringify(pack)); - new_monster(pack.i,pack); - broadcast("game_event",{name:"wabbit",map:pack.i}); + new_monster(pack.i, pack); + broadcast("game_event", { name: "wabbit", map: pack.i }); } - if(type=="goldenbat") - { - var packs=[]; - ["cave"].forEach(function(m){ - G.maps[m].monsters.forEach(function(p){ - if(!p.boundaries) packs.push({"type":"goldenbat",boundary:p.boundary,count:1,i:m,roam:true}); + if (type == "goldenbat") { + var packs = []; + ["cave"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "goldenbat", boundary: p.boundary, count: 1, i: m, roam: true }); + } }); }); - var pack=packs[floor(Math.random()*packs.length)]; - pack.gold=D.monster_gold.goldenbat; + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.goldenbat; //server_log("Love Goo: "+JSON.stringify(pack)); - new_monster(pack.i,pack); + new_monster(pack.i, pack); // broadcast("game_event",{name:"goldenbat",map:pack.i}); } - if(type=="cutebee") - { - var packs=[]; - ["main"].forEach(function(m){ - G.maps[m].monsters.forEach(function(p){ - if(!p.boundaries) packs.push({"type":"cutebee",boundary:p.boundary,count:1,i:m,roam:true}); + if (type == "cutebee") { + var packs = []; + ["main"].forEach(function (m) { + G.maps[m].monsters.forEach(function (p) { + if (!p.boundaries) { + packs.push({ type: "cutebee", boundary: p.boundary, count: 1, i: m, roam: true }); + } }); }); - var pack=packs[floor(Math.random()*packs.length)]; - pack.gold=D.monster_gold.cutebee; + var pack = packs[floor(Math.random() * packs.length)]; + pack.gold = D.monster_gold.cutebee; //server_log("Love Goo: "+JSON.stringify(pack)); - new_monster(pack.i,pack); + new_monster(pack.i, pack); } } -function collect_signups(event) -{ - var names=[],toggle=0; - for(var name in signups) names.push(name); shuffle(names); - names.forEach(function(name){ - var player=players[name_to_id[name]]; if(!player) return; +function collect_signups(event) { + var names = []; + var toggle = 0; + for (var name in signups) { + names.push(name); + } + shuffle(names); + names.forEach(function (name) { + var player = players[name_to_id[name]]; + if (!player) { + return; + } - if(event=="abtesting") player.team=(toggle==1&&"A")||"B",toggle=1-toggle; + if (event == "abtesting") { + player.team = (toggle == 1 && "A") || "B"; + toggle = 1 - toggle; + } - if(event=="abtesting" && player.team=="A") transport_player_to(player,event,2),resend(player,"cid"); - else if(event=="abtesting" && player.team=="B") transport_player_to(player,event,3),resend(player,"cid"); - else transport_player_to(player,event); + if (event == "abtesting" && player.team == "A") { + transport_player_to(player, event, 2); + resend(player, "cid"); + } else if (event == "abtesting" && player.team == "B") { + transport_player_to(player, event, 3); + resend(player, "cid"); + } else { + transport_player_to(player, event); + } }); } -var last_daily=null; -function event_loop() -{ try { - var c=new Date(),ch=(c.getUTCHours()+24+E.schedule.time_offset)%24; - var change=false; - if(ch>=0 && ch<=5) E.schedule.night=true; - else E.schedule.night=false; - - E.schedule.dailies.forEach(function(h){ - if(ch==h && last_daily!=h && msince(server_start)>2) - { - last_daily=h; - var event=dailies.shift(); - dailies.push(event); - events[event]=true; +var last_daily = null; +function event_loop() { + try { + var c = new Date(); + var ch = (c.getUTCHours() + 24 + E.schedule.time_offset) % 24; + var change = false; + if (ch >= 0 && ch <= 5) { + E.schedule.night = true; + } else { + E.schedule.night = false; } - }); - E.schedule.nightlies.forEach(function(h){ - if(ch==h && last_daily!=h && msince(server_start)>2) - { - last_daily=h; - var event=nightlies.shift(); - nightlies.push(event); - events[event]=true; - } - }); - - if(events.halloween) - { - var s=get_monster("slenderman"); - if(s) - { - var p=false; - for(var id in projectiles) - { - if(projectiles[id].target==s) p=true; + E.schedule.dailies.forEach(function (h) { + if (ch == h && last_daily != h && msince(server_start) > 2) { + last_daily = h; + var event = dailies.shift(); + dailies.push(event); + events[event] = true; } - if(!s.last_jump || msince(s.last_jump)>2) p=true; - for(var name in instances[s.in].players) - { - var player=instances[s.in].players[name]; - if(p || !player.s.invis && distance(player,s)<600) - { - xy_emit(s,"disappear",{id:s.id}); - delete instances[s.in].monsters[s.id]; - s.oin=s.in=s.map=random_one(["spookytown","halloween","cave"]); - instances[s.in].monsters[s.id]=s; - var p=random_place(s.map); - s.moving=false; s.abs=true; - s.map_def.boundary=[p.x,p.y,p.x,p.y]; s.map_def.i=s.map; - s.x=p.x; - s.y=p.y; - s.u=true; s.cid++; - s.last_jump=new Date(); - break; + }); + + E.schedule.nightlies.forEach(function (h) { + if (ch == h && last_daily != h && msince(server_start) > 2) { + last_daily = h; + var event = nightlies.shift(); + nightlies.push(event); + events[event] = true; + } + }); + + if (events.halloween) { + var s = get_monster("slenderman"); + if (s) { + var p = false; + for (var id in projectiles) { + if (projectiles[id].target == s) { + p = true; + } + } + if (!s.last_jump || msince(s.last_jump) > 2) { + p = true; + } + for (var name in instances[s.in].players) { + var player = instances[s.in].players[name]; + if (p || (!player.s.invis && distance(player, s) < 600)) { + xy_emit(s, "disappear", { id: s.id }); + delete instances[s.in].monsters[s.id]; + s.oin = s.in = s.map = random_one(["spookytown", "halloween", "cave"]); + instances[s.in].monsters[s.id] = s; + var p = random_place(s.map); + s.moving = false; + s.abs = true; + s.map_def.boundary = [p.x, p.y, p.x, p.y]; + s.map_def.i = s.map; + s.x = p.x; + s.y = p.y; + s.u = true; + s.cid++; + s.last_jump = new Date(); + break; + } } } } - } - if(monster_c.tiger) - { - var m=get_monster("tiger"); - var ps=[]; - for(var id in players) ps.push(players[id]); - var player=random_one(ps); - var spot=null,p=false; - for(var id in projectiles) - { - if(projectiles[id].target==m && projectiles[id].attack>999) p=true; - } - if(p || Math.random()<0.01) - { - if(player) spot=safe_xy_nearby(player.map,player.x,player.y); - if(m && player && player.in==player.map && spot && !G.maps[player.map].mount && !player.s.hopsickness && player.p.home==region+server_name) - { - xy_emit(m,"disappear",{id:m.id}); - delete instances[m.in].monsters[m.id]; - m.oin=m.in=m.map=player.map; - instances[m.in].monsters[m.id]=m; - m.moving=false; m.abs=true; - m.x=spot.x; m.y=spot.y; - m.map_def.boundary=[m.x,m.y,m.x,m.y]; m.map_def.i=m.map; - m.u=true; m.cid++; - m.last_jump=new Date(); + if (monster_c.tiger) { + var m = get_monster("tiger"); + var ps = []; + for (var id in players) { + ps.push(players[id]); } - } - } - ["crabxx","franky","icegolem"].forEach(function(name){ - if(events[name]) - { - - var monster=get_monster(name); - if(!monster && monster_c[name]) return; // irregular move - if(!timers[name]) - { - timers[name]=future_s(G.events[name].duration); - spawn_special_monster(name); - } - else if(c>timers[name] && (!monster || !monster.last.attacked || ssince(monster.last.attacked)>20)) - { - if(monster) remove_monster(monster,{method:"disappear"}); - broadcast("notice",{message:G.monsters[name].name+" Event is over ..."}); - events[name]=false; change=true; delete E[name]; delete timers[name]; - } - else - { - var monster=get_monster(name) - if(!monster) - { - broadcast("notice",{message:G.monsters[name].name+" has been defeated!"}); - events[name]=false; change=true; delete E[name]; delete timers[name]; + var player = random_one(ps); + var spot = null; + var p = false; + for (var id in projectiles) { + if (projectiles[id].target == m && projectiles[id].attack > 999) { + p = true; } - else - { - if(!E[name]) change=true; - E[name]={live:true,map:monster.map,hp:monster.hp,max_hp:monster.max_hp,target:monster.target,x:monster.x,y:monster.y,end:timers[name]}; - if(name=="crabxx") - { - var big=get_monster("crabx"); - if(monster && big && !monster["1hp"]) - { - monster["1hp"]=true; - monster.s={}; - monster.cid++; monster.u=true; + } + if (p || Math.random() < 0.01) { + if (player) { + spot = safe_xy_nearby(player.map, player.x, player.y); + } + if ( + m && + player && + player.in == player.map && + spot && + !G.maps[player.map].mount && + !player.s.hopsickness && + player.p.home == region + server_name + ) { + xy_emit(m, "disappear", { id: m.id }); + delete instances[m.in].monsters[m.id]; + m.oin = m.in = m.map = player.map; + instances[m.in].monsters[m.id] = m; + m.moving = false; + m.abs = true; + m.x = spot.x; + m.y = spot.y; + m.map_def.boundary = [m.x, m.y, m.x, m.y]; + m.map_def.i = m.map; + m.u = true; + m.cid++; + m.last_jump = new Date(); + } + } + } + ["crabxx", "franky", "icegolem"].forEach(function (name) { + if (events[name]) { + var monster = get_monster(name); + if (!monster && monster_c[name]) { + return; + } // irregular move + if (!timers[name]) { + timers[name] = future_s(G.events[name].duration); + spawn_special_monster(name); + } else if (c > timers[name] && (!monster || !monster.last.attacked || ssince(monster.last.attacked) > 20)) { + if (monster) { + remove_monster(monster, { method: "disappear" }); + } + broadcast("notice", { message: G.monsters[name].name + " Event is over ..." }); + events[name] = false; + change = true; + delete E[name]; + delete timers[name]; + } else { + var monster = get_monster(name); + if (!monster) { + broadcast("notice", { message: G.monsters[name].name + " has been defeated!" }); + events[name] = false; + change = true; + delete E[name]; + delete timers[name]; + } else { + if (!E[name]) { + change = true; } - else if(monster && !big && monster["1hp"]) - { - monster["1hp"]=false; - monster.cid++; monster.u=true; + E[name] = { + live: true, + map: monster.map, + hp: monster.hp, + max_hp: monster.max_hp, + target: monster.target, + x: monster.x, + y: monster.y, + end: timers[name], + }; + if (name == "crabxx") { + var big = get_monster("crabx"); + if (monster && big && !monster["1hp"]) { + monster["1hp"] = true; + monster.s = {}; + monster.cid++; + monster.u = true; + } else if (monster && !big && monster["1hp"]) { + monster["1hp"] = false; + monster.cid++; + monster.u = true; + } } } } } + }); + + if (events.goobrawl) { + if (!timers.goobrawl) { + timers.goobrawl = future_s(G.events.goobrawl.duration); + E.goobrawl = { end: timers.goobrawl }; + change = true; + //create_instance("goobrawl","goobrawl",{event:true}); + // collect_signups("goobrawl"); + broadcast("notice", { message: "Goo Brawl has begun!" }); + } else if (c > timers.goobrawl) { + events.goobrawl = false; + delete E.goobrawl; + delete timers.goobrawl; + change = true; + //destroy_instance("goobrawl"); + broadcast("notice", { message: "Goo Brawl is over, hope you had fun!" }); + } else if (instances.goobrawl && Object.keys(instances.goobrawl.monsters).length < 6 && Math.random() < 0.3) { + if (Math.random() < 0.01) { + var data = clone(G.maps.goobrawl.monsters[0]); + data.type = "rgoo"; + var m = new_monster("goobrawl", data); + //m.s.filter={ms:9999999,name:"scale",scale:2}; + } else { + var data = clone(G.maps.goobrawl.monsters[0]); + data.type = "bgoo"; + var m = new_monster("goobrawl", data); + m.skin = random_one(["goo0", "goo1", "goo2", "goo3", "goo4", "goo5", "goo6"]); + // m.drops=[[0.5,"funtoken"]]; + m.u = true; + m.cid++; + } + } + } + + if (gameplay == "hardcore" && ssince(timers.hardcore) > 64) { + timers.hardcore = new Date(); + for (var id in instances.woffice.players) { + var player = instances.woffice.players[id]; + var gold = 480000; + player.gold += gold; + player.socket.emit("game_log", "Received " + to_pretty_num(gold) + " gold"); + player.socket.emit("disappearing_text", { + message: "+" + gold, + x: player.x, + y: player.y - 32, + args: { color: "+gold", size: "large" }, + }); + resend(player, "reopen"); + } + } + + if (events.goldenbat && stats.kills.bat > edges.next_goldenbat) { + edges.next_goldenbat += parseInt(events.goldenbat * Math.random()); + spawn_special_monster("goldenbat"); + } + + if (events.cutebee && stats.kills.bee > edges.next_cutebee) { + edges.next_cutebee += parseInt(events.cutebee * Math.random()); + spawn_special_monster("cutebee"); + } + + if (!events.holidayseason && events.snowman && !monster_c.snowman) { + if (!timers.snowman) { + timers.snowman = future_s(events.snowman * 60); + } else if (timers.snowman && c > timers.snowman) { + timers.snowman = 0; + spawn_special_monster("snowman"); + } + } + + var eventmap = [ + ["halloween", "mrpumpkin"], + ["halloween", "mrgreen"], + ["halloween", "slenderman", true], + ["holidayseason", "grinch"], + ["holidayseason", "snowman"], + ["lunarnewyear", "dragold"], + ["lunarnewyear", "tiger", true], + ["valentines", "pinkgoo", true], + ["egghunt", "wabbit", true], + ]; + eventmap.forEach(function (s) { + if (events[s[0]] && !monster_c[s[1]]) { + if (!timers[s[1]]) { + if (timers[s[1]] !== 0) { + timers[s[1]] = future_s(120); + } else { + timers[s[1]] = future_s(G.monsters[s[1]].respawn); + } + E[s[1]] = { live: false, spawn: timers[s[1]] }; + broadcast_e(); + } else if (timers[s[1]] && c > timers[s[1]]) { + timers[s[1]] = 0; + spawn_special_monster(s[1]); + var m = get_monsters(s[1])[0]; + var data = { live: true, map: m.map, hp: m.hp, max_hp: m.max_hp, target: m.target }; + if (!s[2]) { + data.x = m.x; + data.y = m.y; + } + E[s[1]] = data; + broadcast_e(); + } + } + }); + ["holidayseason", "halloween", "lunarnewyear", "valentines", "egghunt"].forEach(function (event) { + if (events[event]) { + E[event] = true; + } else if (E[event]) { + delete E[event]; + } + }); + ["snowman", "grinch", "dragold", "mrpumpkin", "mrgreen", "wabbit", "pinkgoo", "tiger"].forEach(function (type) { + if (E[type] && !monster_c[type]) { + delete E[type]; // [20/01/23] + change = true; + } + }); + for (var name in instances) { + for (var id in instances[name].monsters) { + var monster = instances[name].monsters[id]; + ["tiger"].forEach(function (type) { + if (monster.type == type) { + E[type] = { live: true, map: monster.map, hp: monster.hp, max_hp: monster.max_hp, target: monster.target }; + } + }); + ["snowman", "grinch", "dragold", "mrpumpkin", "mrgreen", "wabbit", "pinkgoo"].forEach(function (type) { + if (monster.type == type) { + if (!E[type]) { + change = true; + } + E[type] = { + live: true, + map: monster.map, + hp: monster.hp, + max_hp: monster.max_hp, + target: monster.target, + x: monster.x, + y: monster.y, + }; + } + }); + if (monster.type == "grinch") { + if (instances[monster.in].paused) { + resume_instance(instances[monster.in]); + } + + var phrase = null; + var disengage = false; + + if (Math.random() < 0.1) { + if (monster.target && !G.monsters.grinch.good) { + phrase = random_one([ + "Come to papa", + "This is not a chew toy!", + "Give me that! Don't you know you're not supposed to take things that don't belong to you? What's the matter with you? You some kind of wild animal?", + "HELP ME…I'm FEELING.", + "It came without ribbons, it came without tags. It came without packages, boxes, or bags.", + "Poor, poor, " + monster.target, + "Innie, minnie, tiny " + monster.target + "innie", + ]); + } else if (!monster.target) { + phrase = random_one([ + "That is not a chew toy!", + "Stupid. Ugly. Out of date. This is ridiculous. If I can't find something nice to wear I'm not going.", + "Kids today. So desensitized by movies and television.", + "Holiday who-be what-ee?", + "I could use a little social interaction.", + "It's because I'm green isn't it?", + "Social distancing what?", + ]); + } + } + + if ( + monster.target && + get_player(monster.target) && + get_player(monster.target).slots.chest && + get_player(monster.target).slots.chest.name == "xmassweater" && + !G.monsters.grinch.good + ) { + phrase = "Ugh. What's that ugly thing you are wearing?! I can't look at it. Stop."; + disengage = true; + } + + if (phrase) { + xy_emit(monster, "chat_log", { owner: "Grinch", message: phrase, id: monster.id, color: "#418343" }); + } + + if (!monster.target && Math.random() < 0.1 * Object.keys(players).length) { + monster.last_attack = future_ms(1000); + var player = random_one(players); + if ( + (player.level < 50 && Math.random() > 0.08) || + (G.maps[player.map] && + (G.maps[player.map].safe || G.maps[player.map].instance || G.maps[player.map].irregular)) + ) { + player = null; + } + if (player && 0) { + // attack everyone + for (var pid in instances[player.in].players) { + if (!player) { + break; + } + var ally = instances[player.in].players[pid]; + if ( + ally.name != player.name && + !ally.npc && + (ally.type != "merchant" || player.type == "merchant") && + simple_distance(ally, player) < 500 + ) { + player = null; + break; + } + } + } + if (player) { + target_player(monster, player); + } + } + if ((monster.target && !is_disabled(monster) && Math.random() < 0.05) || disengage) { + stop_pursuit(monster, { force: true, cause: "disengage" }); + } + } + } + } + + eventmap.forEach(function (s) { + if (events[s[0]] && !E[s[1]]) { + E[s[1]] = { live: false, spawn: timers[s[1]] }; + change = true; + } + }); + + if (events.goblin) { + } + + if (events.hide_and_seek) { + } + + if (events.abtesting) { + if (timers.abtesting && c > timers.abtesting) { + var winner = "A"; + if (E.abtesting.B > E.abtesting.A) { + winner = "B"; + } + events.abtesting = false; + timers.abtesting = false; + delete E.abtesting; + change = true; + broadcast("server_message", { message: "Team " + winner + " wins! Hope you all had fun!", color: "#4BB6E1" }); + for (var id in instances.abtesting.players) { + var player = instances.abtesting.players[id]; + var table = "abtesting"; + if (!player.team) { + continue; + } + if (player.team != winner) { + table = "abtesting_loser"; + } + if (!player.esize) { + socket.emit("game_log", "Full inventory. Unable to receive a prize."); + continue; + } + exchange(player, table); + } + destroy_instance("abtesting"); + } else if (!timers.abtesting) { + timers.abtesting = future_s(G.events.abtesting.duration); + change = true; + timers.abtesting_start = c; + E.abtesting = { end: timers.abtesting, signup_end: future_s(60), A: 0, B: 0, id: randomStr(5) }; + create_instance("abtesting", "abtesting", { event: true }); + collect_signups("abtesting"); + broadcast("server_message", { message: "A/B Testing has begun!", color: "#4BB6E1" }); + // npcs.bean.s.invis={ms:999999999999}; xy_emit(npcs.bean,"disappear",{id:"Bean"}); + } + } + + if (instances.cyberland && ssince(timers.cyberland) > 10) { + var detected = false; + timers.cyberland = new Date(); + for (var id in instances.cyberland.monsters) { + var monster = instances.cyberland.monsters[id]; + if (monster.target) { + detected = get_player(monster.target); + } + } + if (detected) { + xy_emit({ in: "cyberland", map: "cyberland", x: 0, y: -100 }, "chat_log", { + owner: "mainframe", + message: "ALERT", + id: "mainframe", + }); + for (var id in instances.cyberland.monsters) { + var monster = instances.cyberland.monsters[id]; + if (!monster.target) { + target_player(monster, detected); + } + } + } + if (Math.random() < 1.0 / 1600) { + S.misc.spares.push("electronics"); + if (Math.random() < 1.0 / 1020) { + S.misc.spares.push("networkcard"); + } + if (S.misc.spares.length > 2000) { + S.misc.spares.length = 2000; + } + } + } + + for (var id in E.duels || {}) { + var duel = E.duels[id]; + var instance = instances[id]; + var info = instance.info; + var a = 0; + var b = 0; + if (info.seconds) { + info.seconds--; + } + // console.log(info.A); console.log(info.B); + for (var i = 0; i < info.A.length; i++) { + var p = info.A[i]; + var player = get_player(p.name); + //console.log(player.in+" "+player.duel.id+" "+player.rip); + if (!player || player.in != id || !player.duel || player.rip || !player.team) { + p.active = false; + } + if (p.active) { + info.A[i] = player_to_summary(player); + a++; + info.A[i].active = true; + } + } + for (var i = 0; i < info.B.length; i++) { + var p = info.B[i]; + var player = get_player(p.name); + if (!player || player.in != id || !player.duel || player.rip || !player.team) { + p.active = false; + } + if (p.active) { + info.B[i] = player_to_summary(player); + b++; + info.B[i].active = true; + } + } + if (!info.seconds && !info.active) { + duel.a = []; + duel.b = []; + info.active = true; + info.A.forEach(function (p) { + var player = get_player(p.name); + if (player && p.active) { + player.s = {}; + duel.a.push(p.name); + resend(player, "u+cid"); + } + }); + info.B.forEach(function (p) { + var player = get_player(p.name); + if (player && p.active) { + player.s = {}; + duel.b.push(p.name); + resend(player, "u+cid"); + } + }); + } + if (!a || !b) { + var message = duel.challenger + " wins the duel!"; + var chat = duel.challenger + " defeated " + duel.vs + "!"; + var sent = {}; + if (!a) { + message = duel.vs + " wins the duel!"; + chat = duel.vs + " defeated " + duel.challenger + "!"; + } + for (var pid in instance.players) { + sent[instance.players[pid].name] = true; + instance.players[pid].socket.emit("game_chat", { message: message, color: "#47C1AE" }); + } + info.A.forEach(function (p) { + var player = get_player(p.name); + if (player && !sent[p.name]) { + player.socket.emit("game_chat", { message: message, color: "#47C1AE" }); + } + }); + info.B.forEach(function (p) { + var player = get_player(p.name); + if (player && !sent[p.name]) { + player.socket.emit("game_chat", { message: message, color: "#47C1AE" }); + } + }); + + destroy_instance(id); + + if (!a) { + info.B.forEach(function (p) { + var player = get_player(p.name); + if (player) { + xy_emit(player, "eval", "confetti_shower(get_player('" + player.name + "'),2)"); + } + }); + } else { + info.A.forEach(function (p) { + var player = get_player(p.name); + if (player) { + xy_emit(player, "eval", "confetti_shower(get_player('" + player.name + "'),2)"); + } + }); + } + + if (!a && get_player(duel.vs)) { + xy_emit(get_player(duel.vs), "game_chat", { message: chat, color: "#47C1AE" }); + } + if (!b && get_player(duel.challenger)) { + xy_emit(get_player(duel.challenger), "game_chat", { message: chat, color: "#47C1AE" }); + } + + delete E.duels[id]; + broadcast_e(); + continue; + } + duel.active = instance.active; + duel.seconds = instance.seconds; + instance_emit(id, "map_info", instance.info); + } + + for (var e in E) { + if ((E[e] && E[e].target) || Object.keys(E.duels || {}).length) { + change = true; + break; + } + } + if (change) { + broadcast_e(); + } + } catch (e) { + log_trace("Critical-event_loop: ", e); + } +} + +function broadcast_e(dont_send) { + if (!dont_send) { + broadcast("server_info", E); + } +} + +function start_event(name) { + if (name == "goblin") { + events.goblin = true; + } else if (name == "goobrawl") { + signups = {}; + events.goobrawl = 1; + timers.goobrawl = future_s(45); + broadcast("notice", { message: "Goo Brawl is about to start!" }); + } else if (name == "abtesting") { + signups = {}; + events.abtesting = 1; + timers.abtesting = future_s(45); + // instances.main.players[NPC_prefix+"Bean"]=npcs.bean; + // npcs.bean.party="abtesting"; npcs.bean.last.move=new Date(); + broadcast("server_message", { message: "A/B Testing is about to start!", color: "#4BB6E1" }); + } +} + +function new_worker(num) { + var worker = new Worker(variables.worker_path, { + workerData: { G: G, amap_data: amap_data, smap_data: smap_data }, + env: SHARE_ENV, + execArgv: [], + }); + worker.wnum = num; + worker.on("message", function (data) { + if (data.type == "monster_move") { + var monster = instances[data.in].monsters[data.id]; + if (!monster) { + return; + } + monster.working = false; + if (data.move && (data.move[2] != "bad" || (monster.bmoves || 0) < 6)) { + if (data.move[2] == "bad") { + monster.bmoves = (monster.bmoves || 0) + 1; + } else { + monster.bmoves = 0; + } + monster.going_x = data.move[0]; + monster.going_y = data.move[1]; + start_moving_element(monster); + } else { + var player = monster.target && get_player(monster.target); + if (player) { + if (monster.attack < 120 || distance(monster, player, true) > monster.range) { + stop_pursuit(monster, { cause: "cant_move_smart" }); + } + } + } } }); - - if(events.goobrawl) - { - if(!timers.goobrawl) - { - timers.goobrawl=future_s(G.events.goobrawl.duration); E.goobrawl={end:timers.goobrawl}; change=true; - //create_instance("goobrawl","goobrawl",{event:true}); - // collect_signups("goobrawl"); - broadcast("notice",{message:"Goo Brawl has begun!"}); - } - else if(c>timers.goobrawl) - { - events.goobrawl=false; delete E.goobrawl; delete timers.goobrawl; change=true; - //destroy_instance("goobrawl"); - broadcast("notice",{message:"Goo Brawl is over, hope you had fun!"}); - } - else if(instances.goobrawl && Object.keys(instances.goobrawl.monsters).length<6 && Math.random()<0.3) - { - if(Math.random()<0.01) - { - var data=clone(G.maps.goobrawl.monsters[0]); - data.type="rgoo"; - var m=new_monster("goobrawl",data); - //m.s.filter={ms:9999999,name:"scale",scale:2}; - } - else - { - var data=clone(G.maps.goobrawl.monsters[0]); - data.type="bgoo"; - var m=new_monster("goobrawl",data); - m.skin=random_one(["goo0","goo1","goo2","goo3","goo4","goo5","goo6"]); - // m.drops=[[0.5,"funtoken"]]; - m.u=true; m.cid++; - } - } - } - - if(gameplay=="hardcore" && ssince(timers.hardcore)>64) - { - timers.hardcore=new Date(); - for(var id in instances.woffice.players) - { - var player=instances.woffice.players[id],gold=480000; - player.gold+=gold; - player.socket.emit("game_log","Received "+to_pretty_num(gold)+" gold"); - player.socket.emit("disappearing_text",{"message":"+"+gold,"x":player.x,"y":player.y-32,"args":{color:"+gold","size":"large"}}); - resend(player,"reopen"); - } - } - - if(events.goldenbat && stats.kills.bat>edges.next_goldenbat) - { - edges.next_goldenbat+=parseInt(events.goldenbat*Math.random()); - spawn_special_monster("goldenbat"); - } - - if(events.cutebee && stats.kills.bee>edges.next_cutebee) - { - edges.next_cutebee+=parseInt(events.cutebee*Math.random()); - spawn_special_monster("cutebee"); - } - - if(!events.holidayseason && events.snowman && !monster_c.snowman) - { - if(!timers.snowman) timers.snowman=future_s(events.snowman*60); - else if(timers.snowman && c>timers.snowman) timers.snowman=0,spawn_special_monster("snowman"); - } - - var eventmap=[ - ["halloween","mrpumpkin"], - ["halloween","mrgreen"], - ["halloween","slenderman",true], - ["holidayseason","grinch"], - ["holidayseason","snowman"], - ["lunarnewyear","dragold"], - ["lunarnewyear","tiger",true], - ["valentines","pinkgoo",true], - ["egghunt","wabbit",true], - ]; - eventmap.forEach(function(s){ - if(events[s[0]] && !monster_c[s[1]]) - { - if(!timers[s[1]]) - { - if(timers[s[1]]!==0) timers[s[1]]=future_s(120); - else timers[s[1]]=future_s(G.monsters[s[1]].respawn); - E[s[1]]={live:false,spawn:timers[s[1]]}; - broadcast_e(); - } - else if(timers[s[1]] && c>timers[s[1]]) - { - timers[s[1]]=0; - spawn_special_monster(s[1]); - var m=get_monsters(s[1])[0]; - var data={live:true,map:m.map,hp:m.hp,max_hp:m.max_hp,target:m.target}; - if(!s[2]) data.x=m.x,data.y=m.y; - E[s[1]]=data; - broadcast_e(); - } - } + worker.on("error", function (data) { + console.log("#W Worker error: " + JSON.stringify(data)); }); - ["holidayseason","halloween","lunarnewyear","valentines","egghunt"].forEach(function(event){ - if(events[event]) E[event]=true; - else if(E[event]) delete E[event]; - }); - ["snowman","grinch","dragold","mrpumpkin","mrgreen","wabbit","pinkgoo","tiger"].forEach(function(type){ - if(E[type] && !monster_c[type]) - { - delete E[type]; // [20/01/23] - change=true; - } - }); - for(var name in instances) - { - for(var id in instances[name].monsters) - { - var monster=instances[name].monsters[id]; - ["tiger"].forEach(function(type){ - if(monster.type==type) - { - E[type]={live:true,map:monster.map,hp:monster.hp,max_hp:monster.max_hp,target:monster.target}; - } - }); - ["snowman","grinch","dragold","mrpumpkin","mrgreen","wabbit","pinkgoo"].forEach(function(type){ - if(monster.type==type) - { - if(!E[type]) change=true; - E[type]={live:true,map:monster.map,hp:monster.hp,max_hp:monster.max_hp,target:monster.target,x:monster.x,y:monster.y}; - } - }); - if(monster.type=="grinch") - { - if(instances[monster.in].paused) resume_instance(instances[monster.in]); - - var phrase=null,disengage=false; - - if(Math.random()<0.1) - { - if(monster.target && !G.monsters.grinch.good) phrase=random_one(["Come to papa","This is not a chew toy!","Give me that! Don't you know you're not supposed to take things that don't belong to you? What's the matter with you? You some kind of wild animal?","HELP ME…I'm FEELING.","It came without ribbons, it came without tags. It came without packages, boxes, or bags.","Poor, poor, "+monster.target,"Innie, minnie, tiny "+monster.target+"innie"]); - else if(!monster.target) phrase=random_one(["That is not a chew toy!","Stupid. Ugly. Out of date. This is ridiculous. If I can't find something nice to wear I'm not going.","Kids today. So desensitized by movies and television.","Holiday who-be what-ee?","I could use a little social interaction.","It's because I'm green isn't it?","Social distancing what?"]) - } - - if(monster.target && get_player(monster.target) && get_player(monster.target).slots.chest && get_player(monster.target).slots.chest.name=="xmassweater" && !G.monsters.grinch.good) - phrase="Ugh. What's that ugly thing you are wearing?! I can't look at it. Stop.",disengage=true; - - if(phrase) xy_emit(monster,"chat_log",{owner:"Grinch",message:phrase,id:monster.id,color:"#418343"}); - - - if(!monster.target && Math.random()<0.1*Object.keys(players).length) - { - monster.last_attack=future_ms(1000); - var player=random_one(players); - if(player.level<50 && Math.random()>0.08 || G.maps[player.map] && (G.maps[player.map].safe || G.maps[player.map].instance || G.maps[player.map].irregular)) player=null; - if(player && 0) // attack everyone - for(var pid in instances[player.in].players) - { - if(!player) break; - var ally=instances[player.in].players[pid]; - if(ally.name!=player.name && !ally.npc && (ally.type!="merchant" || player.type=="merchant") && simple_distance(ally,player)<500) - { - player=null; - break; - } - } - if(player) - target_player(monster,player); - } - if(monster.target && !is_disabled(monster) && Math.random()<0.05 || disengage) - stop_pursuit(monster,{force:true,cause:"disengage"}); - } - } - } - - eventmap.forEach(function(s){ - if(events[s[0]] && !E[s[1]]) - E[s[1]]={live:false,spawn:timers[s[1]]},change=true; - }); - - if(events.goblin) - { - - } - - if(events.hide_and_seek) - { - - } - - if(events.abtesting) - { - if(timers.abtesting && c>timers.abtesting) - { - var winner="A"; - if(E.abtesting.B>E.abtesting.A) winner="B"; - events.abtesting=false; timers.abtesting=false; delete E.abtesting; change=true; - broadcast("server_message",{message:"Team "+winner+" wins! Hope you all had fun!",color:"#4BB6E1"}); - for(var id in instances.abtesting.players) - { - var player=instances.abtesting.players[id],table="abtesting"; - if(!player.team) continue; - if(player.team!=winner) table="abtesting_loser"; - if(!player.esize) { socket.emit("game_log","Full inventory. Unable to receive a prize."); continue;} - exchange(player,table); - } - destroy_instance("abtesting"); - } - else if(!timers.abtesting) - { - timers.abtesting=future_s(G.events.abtesting.duration); change=true; - timers.abtesting_start=c; - E.abtesting={end:timers.abtesting,signup_end:future_s(60),A:0,B:0,id:randomStr(5)}; - create_instance("abtesting","abtesting",{event:true}); - collect_signups("abtesting"); - broadcast("server_message",{message:"A/B Testing has begun!",color:"#4BB6E1"}); - // npcs.bean.s.invis={ms:999999999999}; xy_emit(npcs.bean,"disappear",{id:"Bean"}); - } - } - - if(instances.cyberland && ssince(timers.cyberland)>10) - { - var detected=false; - timers.cyberland=new Date(); - for(var id in instances.cyberland.monsters) - { - var monster=instances.cyberland.monsters[id]; - if(monster.target) detected=get_player(monster.target); - } - if(detected) - { - xy_emit({"in":"cyberland","map":"cyberland","x":0,"y":-100},"chat_log",{owner:"mainframe",message:"ALERT",id:"mainframe"}); - for(var id in instances.cyberland.monsters) - { - var monster=instances.cyberland.monsters[id]; - if(!monster.target) target_player(monster,detected); - } - } - if(Math.random()<1.0/1600) - { - S.misc.spares.push("electronics"); - if(Math.random()<1.0/1020) - S.misc.spares.push("networkcard"); - if(S.misc.spares.length>2000) S.misc.spares.length=2000; - } - } - - for(var id in (E.duels||{})) - { - var duel=E.duels[id]; - var instance=instances[id]; - var info=instance.info,a=0,b=0; - if(info.seconds) info.seconds--; - // console.log(info.A); console.log(info.B); - for(var i=0;imonster.range) stop_pursuit(monster,{cause:"cant_move_smart"}); - } - } - }); - worker.on('error',function(data){ - console.log("#W Worker error: "+JSON.stringify(data)); - }); - worker.on('exit',function(code){ - workers[this.wnum]=new_worker(this.wnum); + worker.on("exit", function (code) { + workers[this.wnum] = new_worker(this.wnum); }); return worker; } -function init_server() -{ - if(gameplay=="hardcore") - { - E={ - "rewards":{ - "item8":null, - "item9":null, - "item10":null, - "item11":null, - "item12":null, - "leader":null, - "first_fvampire":null, - "first_mvampire":null, - "first_skeletor":null, - "first_stompy":null, - "first_franky":null, - "first_ent":null, - "first_wabbit":null, - "accessory5":null, - "accessory6":null, - "first_warrior_70":null, - "first_paladin_70":null, - "first_priest_70":null, - "first_mage_70":null, - "first_ranger_70":null, - "first_rogue_70":null, - "!participation":"Every authorized account with a level 60+ character receives a participation award!", +function init_server() { + if (gameplay == "hardcore") { + E = { + rewards: { + item8: null, + item9: null, + item10: null, + item11: null, + item12: null, + leader: null, + first_fvampire: null, + first_mvampire: null, + first_skeletor: null, + first_stompy: null, + first_franky: null, + first_ent: null, + first_wabbit: null, + accessory5: null, + accessory6: null, + first_warrior_70: null, + first_paladin_70: null, + first_priest_70: null, + first_mage_70: null, + first_ranger_70: null, + first_rogue_70: null, + "!participation": "Every authorized account with a level 60+ character receives a participation award!", }, + }; + E.minutes = 340; + if (is_sdk) { + E.minutes = 12; } - E.minutes=340; - if(is_sdk) E.minutes=12; - setInterval(hardcore_loop,60*1000); + setInterval(hardcore_loop, 60 * 1000); } - setInterval(event_loop,1000); - setInterval(tavern_loop,1000); - workers=[ + setInterval(event_loop, 1000); + setInterval(tavern_loop, 1000); + workers = [ new_worker(0), new_worker(1), new_worker(2), @@ -1979,650 +2571,963 @@ function init_server() ]; } -function init_server_data() -{ - if(!S.dt) S.dt={}; - if(!S.sold) S.sold=[]; - if(!S.found) S.found=[]; - if(!S.gold) S.gold=0; - if(!S.cash) S.cash=0; - if(!S.logs) S.logs={"donate":[],"dice":[]}; - if(!S.misc) S.misc={spares:["electronics"]}; - ["sold","found"].forEach(function(store){ - for(var i=S[store].length-1;i>=0;i--) - if(!S[store][i].name || !G.items[S[store][i].name] || in_arr(S[store][i].name,["cxjar","emotionjar"]) || G.items[S[store][i].name].cash) - S[store].splice(i,1); +function init_server_data() { + if (!S.dt) { + S.dt = {}; + } + if (!S.sold) { + S.sold = []; + } + if (!S.found) { + S.found = []; + } + if (!S.gold) { + S.gold = 0; + } + if (!S.cash) { + S.cash = 0; + } + if (!S.logs) { + S.logs = { donate: [], dice: [] }; + } + if (!S.misc) { + S.misc = { spares: ["electronics"] }; + } + ["sold", "found"].forEach(function (store) { + for (var i = S[store].length - 1; i >= 0; i--) { + if ( + !S[store][i].name || + !G.items[S[store][i].name] || + in_arr(S[store][i].name, ["cxjar", "emotionjar"]) || + G.items[S[store][i].name].cash + ) { + S[store].splice(i, 1); + } + } }); - for(var i=0;i=2) parties[newparty[0]]=newparty; - if(newparty.length<2) - { - if(newparty.length) - { - var player=players[name_to_id[newparty[0]]]; - if(!player) { console.log("#X SHOULD'VE FOUND2 "+newparty[0]); } - else player.party=null; - } + if (newparty.length >= 2) { + parties[newparty[0]] = newparty; } - else - { - newparty.forEach(function(player_name){ - var player=players[name_to_id[player_name]]; - if(!player) { console.log("#X SHOULD'VE FOUND3 "+newparty[0]); return; } - player.party=newparty[0]; + if (newparty.length < 2) { + if (newparty.length) { + var player = players[name_to_id[newparty[0]]]; + if (!player) { + console.log("#X SHOULD'VE FOUND2 " + newparty[0]); + } else { + player.party = null; + } + } + } else { + newparty.forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (!player) { + console.log("#X SHOULD'VE FOUND3 " + newparty[0]); + return; + } + player.party = newparty[0]; }); } // Moved the socket routine to the end, after all party changes are made [10/07/18] - oldparty.forEach(function(player_name){ - var player=players[name_to_id[player_name]]; - if(!player || player.name==leaver.name) return; - newparty=newparty&&parties[newparty[0]]||[]; // During these .socket.emit's, "disconnect"'s happen, the parties can become empty, and party_to_client fails [21/08/18] - player.socket.emit("party_update",{message:leaver.name+" left the party",leave:1,list:newparty.length>=2&&newparty,party:newparty.length>=2&&party_to_client(newparty[0])||{}}); - resend(player,"nc+u+cid"); + oldparty.forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (!player || player.name == leaver.name) { + return; + } + newparty = (newparty && parties[newparty[0]]) || []; // During these .socket.emit's, "disconnect"'s happen, the parties can become empty, and party_to_client fails [21/08/18] + player.socket.emit("party_update", { + message: leaver.name + " left the party", + leave: 1, + list: newparty.length >= 2 && newparty, + party: (newparty.length >= 2 && party_to_client(newparty[0])) || {}, + }); + resend(player, "nc+u+cid"); }); } -function delete_observer(socket) -{ - var observer=observers[socket.id]; +function delete_observer(socket) { + var observer = observers[socket.id]; delete observers[socket.id]; delete instances[observer.in].observers[observer.id]; } -function send_all_xy(observer,args) -{ - var data={players:[],monsters:[],type:'all',in:observer.in,map:observer.map},instance=instances[observer.in]; - for(var id in instance.players) if(!instance.players[id].s.invis && within_xy_range(observer,instance.players[id])) data.players.push(player_to_client(instance.players[id],1)); - for(var id in instance.monsters) if(within_xy_range(observer,instance.monsters[id])) data.monsters.push(monster_to_client(instance.monsters[id])); - observer.last_ux=observer.x; - observer.last_uy=observer.y; - if(observer.moving && mode.xyinf) data.xy={x:observer.x,y:observer.y}; - if(args && args.raw) return data; - observer.socket.emit("entities",data); +function send_all_xy(observer, args) { + var data = { players: [], monsters: [], type: "all", in: observer.in, map: observer.map }; + var instance = instances[observer.in]; + for (var id in instance.players) { + if (!instance.players[id].s.invis && within_xy_range(observer, instance.players[id])) { + data.players.push(player_to_client(instance.players[id], 1)); + } + } + for (var id in instance.monsters) { + if (within_xy_range(observer, instance.monsters[id])) { + data.monsters.push(monster_to_client(instance.monsters[id])); + } + } + observer.last_ux = observer.x; + observer.last_uy = observer.y; + if (observer.moving && mode.xyinf) { + data.xy = { x: observer.x, y: observer.y }; + } + if (args && args.raw) { + return data; + } + observer.socket.emit("entities", data); } -function xy_emit(entity,event,data,must) -{ - var x=entity.x,y=entity.y,owners={}; - for(var id in instances[entity.in].players) - { - var player=instances[entity.in].players[id]; - if(player.npc) continue; - if(player.x-player.vision[0]B.u_boundary) u=true; - else if(abs(element.last_u[1]-element.y)>B.u_boundary) u=true; - - if(u) - { - element.last_u=[element.x,element.y]; - element.u=true; + if (event == "chat_log" && data.p) { + appengine_call("log_chat", { + to: Object.entries(owners), + type: "ambient", + message: data.message, + fro: entity.name, + author: entity.owner, + }); } } -function xy_upush_logic(element) // sets .push so new entities in that area are pushed to the user/element -{ - var u=false; - - if(!element.last_upush) {element.last_upush=[element.x,element.y]; return;} - else if(abs(element.last_upush[0]-element.x)>B.u_vision) u=true; - else if(abs(element.last_upush[1]-element.y)>B.u_vision) u=true; +function xy_u_logic(element) { + // sets .u so updates are sent - this element might enter someone's vision + var u = false; - if(u) - { - element.push=element.last_upush; - element.last_upush=[element.x,element.y]; + if (!element.last_u) { + u = true; + } else if (abs(element.last_u[0] - element.x) > B.u_boundary) { + u = true; + } else if (abs(element.last_u[1] - element.y) > B.u_boundary) { + u = true; + } + + if (u) { + element.last_u = [element.x, element.y]; + element.u = true; } } -function appengine_call(method,args,on_success,on_error) -{ - var api_path="/api/"+method; - if(mode.prevent_external && !in_arr(method,["create_server","update_server","stop_server","start_character","send_mail"])) return; - function retry_call() - { - var t=1600; - args.retried=(args.retried||0)+1; +function xy_upush_logic(element) { + // sets .push so new entities in that area are pushed to the user/element + var u = false; + + if (!element.last_upush) { + element.last_upush = [element.x, element.y]; + return; + } else if (abs(element.last_upush[0] - element.x) > B.u_vision) { + u = true; + } else if (abs(element.last_upush[1] - element.y) > B.u_vision) { + u = true; + } + + if (u) { + element.push = element.last_upush; + element.last_upush = [element.x, element.y]; + } +} + +function appengine_call(method, args, on_success, on_error) { + var api_path = "/api/" + method; + if ( + mode.prevent_external && + !in_arr(method, ["create_server", "update_server", "stop_server", "start_character", "send_mail"]) + ) { + return; + } + function retry_call() { + var t = 1600; + args.retried = (args.retried || 0) + 1; args.retries--; - if(args.retried>20) t=240000; - else if(args.retried>10) t=60000; - else if(args.retried>5) t=20000; - setTimeout(function(){ appengine_call(method,args,on_success,on_error); },t); + if (args.retried > 20) { + t = 240000; + } else if (args.retried > 10) { + t = 60000; + } else if (args.retried > 5) { + t = 20000; + } + setTimeout(function () { + appengine_call(method, args, on_success, on_error); + }, t); // network retries, for "Error: socket hang up" etc. [09/09/16] } - if(!args) args={}; if(!on_success) on_success=function(){}; - if(args.suffix) { api_path+=args.suffix; delete args.suffix; } - data={ 'method':method, 'arguments':JSON.stringify(args), 'server_auth':server_id+"-"+server_auth, 'auth':args.auth }; - try{ - request.post({url:base_url+api_path,form:data},function(err,response,body){ - try{ - if(err || !response || response.statusCode!=200) // node.request sends an undefined response when there is an issue ... - { - console.log("appengine_call - unknown error "+err+" or code: "+(response&&response.statusCode)+" retries: "+args.retries+(new Date())+" on "+api_path); - if(method!="log_error") - setTimeout(function(err,response,api_path,body){ return function(){ - try{ - appengine_call("log_error",{type:"appengine_call_error",err:(response&&response.statusCode)+" "+api_path,info:""+body+""+err+"\n"+JSON.stringify(response&&response.headers||{})}); - }catch(e) - { - log_trace("appengine_call's log_error "+api_path,e); - } - }}(err,response,api_path,body),120000); - if(args.retries) retry_call(); - else if(on_error) on_error(""+err+" "+(response&&response.statusCode)); + if (!args) { + args = {}; + } + if (!on_success) { + on_success = function () {}; + } + if (args.suffix) { + api_path += args.suffix; + delete args.suffix; + } + data = { + method: method, + arguments: JSON.stringify(args), + server_auth: server_id + "-" + server_auth, + auth: args.auth, + }; + try { + request.post({ url: base_url + api_path, form: data }, function (err, response, body) { + try { + if (err || !response || response.statusCode != 200) { + // node.request sends an undefined response when there is an issue ... + console.log( + "appengine_call - unknown error " + + err + + " or code: " + + (response && response.statusCode) + + " retries: " + + args.retries + + new Date() + + " on " + + api_path, + ); + if (method != "log_error") { + setTimeout( + (function (err, response, api_path, body) { + return function () { + try { + appengine_call("log_error", { + type: "appengine_call_error", + err: (response && response.statusCode) + " " + api_path, + info: "" + body + "" + err + "\n" + JSON.stringify((response && response.headers) || {}), + }); + } catch (e) { + log_trace("appengine_call's log_error " + api_path, e); + } + }; + })(err, response, api_path, body), + 120000, + ); + } + if (args.retries) { + retry_call(); + } else if (on_error) { + on_error("" + err + " " + (response && response.statusCode)); + } + } else { + ct = JSON.parse(body); + if (on_success) { + on_success.apply(this, [ct]); + } } - else - { - ct=JSON.parse(body); - if(on_success) on_success.apply(this,[ct]); + } catch (e) { + log_trace("appengine_call exception on " + api_path, e); + if (args.retries) { + retry_call(); + } else if (on_error) { + on_error("" + err + " " + e + " " + (response && response.statusCode)); + } else if (on_success) { + on_success.apply(this, [{ failed: 1, reason: "callbackfailed" }]); } - }catch(e){ - log_trace("appengine_call exception on "+api_path,e); - if(args.retries) retry_call(); - else if(on_error) on_error(""+err+" "+e+" "+(response&&response.statusCode)); - else if(on_success) on_success.apply(this,[{"failed":1,"reason":"callbackfailed"}]); } }); - }catch(e){ - log_trace("appengine_call on "+api_path,e); - if(args.retries) retry_call(); - else if(on_error) on_error(""+e+" "+(response&&response.statusCode)); - else if(on_success) on_success.apply(this,[{"failed":1,"reason":"callfailed"}]); + } catch (e) { + log_trace("appengine_call on " + api_path, e); + if (args.retries) { + retry_call(); + } else if (on_error) { + on_error("" + e + " " + (response && response.statusCode)); + } else if (on_success) { + on_success.apply(this, [{ failed: 1, reason: "callfailed" }]); + } + } + if (!args.init) { + args.init = new Date(); } - if(!args.init) args.init=new Date(); return args; } -function discord_call(message) -{ - if(gameplay=="hardcore" || gameplay=="test") return; - if(is_sdk) return server_log("Discord: "+message); - var url="https://discordapp.com/api/channels/404333059018719233/messages"; - if(message.search(" joined Adventure Land")!=-1) url="https://discordapp.com/api/channels/839163123499794481/messages" +function discord_call(message) { + if (gameplay == "hardcore" || gameplay == "test") { + return; + } + if (is_sdk) { + return server_log("Discord: " + message); + } + var url = "https://discordapp.com/api/channels/404333059018719233/messages"; + if (message.search(" joined Adventure Land") != -1) { + url = "https://discordapp.com/api/channels/839163123499794481/messages"; + } request( { - url:url, - headers:{"Authorization":"Bot "+variables.discord_token}, - method:"POST", - json:{ - "content":message, - } + url: url, + headers: { Authorization: "Bot " + variables.discord_token }, + method: "POST", + json: { + content: message, + }, }, - function(err,response,body){ + function (err, response, body) { //console.log(response); - }); - + }, + ); } -function server_log(message,important) -{ - if(is_sdk || important) - { - - if(is_sdk) console.log(message); - else console.log(message+" ("+new Date()+")"); - if(message && (message+"").indexOf("SEVERE")!=-1) - { - appengine_call("server_event",{ - event:"notice", - keyword:variables.keyword, - id:server_id, - message:message, - color:"red", - },function(result){}); +function server_log(message, important) { + if (is_sdk || important) { + if (is_sdk) { + console.log(message); + } else { + console.log(message + " (" + new Date() + ")"); + } + if (message && (message + "").indexOf("SEVERE") != -1) { + appengine_call( + "server_event", + { + event: "notice", + keyword: variables.keyword, + id: server_id, + message: message, + color: "red", + }, + function (result) {}, + ); } } } -function appengine_log(event,message,color) -{ - if(!color) color="gray"; - appengine_call("server_event",{ - event:event, - keyword:variables.keyword, - id:server_id, - message:message, - color:color, - },function(result){}); +function appengine_log(event, message, color) { + if (!color) { + color = "gray"; + } + appengine_call( + "server_event", + { + event: event, + keyword: variables.keyword, + id: server_id, + message: message, + color: color, + }, + function (result) {}, + ); } -function disappearing_text(socket,entity,text,args) -{ - var x=entity.x,y=entity.y-30,d_args={}; - if(!args) args={}; +function disappearing_text(socket, entity, text, args) { + var x = entity.x; + var y = entity.y - 30; + var d_args = {}; + if (!args) { + args = {}; + } // if(!args.size) args.size=16; - if(!args.color) args.color=""; + if (!args.color) { + args.color = ""; + } - if(args.color) d_args.c=args.color; // color - if(args.s) d_args.s=args.s; // sound - if(args.size) d_args.sz=args.size; - if(args.from) d_args.from=args.from; // for d_text + .evade + d_line + if (args.color) { + d_args.c = args.color; + } // color + if (args.s) { + d_args.s = args.s; + } // sound + if (args.size) { + d_args.sz = args.size; + } + if (args.from) { + d_args.from = args.from; + } // for d_text + .evade + d_line - if(args.xy && args.nv) xy_emit(entity,"disappearing_text",{"message":text,"x":x,"y":y,"id":entity.id,"args":d_args,"nv":1}); - else if(args.xy) xy_emit(entity,"disappearing_text",{"message":text,"x":x,"y":y,"id":entity.id,"args":d_args}); - else if(args.party) party_emit(args.party,"disappearing_text",{"message":text,"x":x,"y":y,"id":entity.id,"args":d_args},{map:args.map}); - else socket.emit("disappearing_text",{"message":text,"x":x,"y":y,"id":entity.id,"args":d_args}); // volatile. + if (args.xy && args.nv) { + xy_emit(entity, "disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args, nv: 1 }); + } else if (args.xy) { + xy_emit(entity, "disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args }); + } else if (args.party) { + party_emit( + args.party, + "disappearing_text", + { message: text, x: x, y: y, id: entity.id, args: d_args }, + { map: args.map }, + ); + } else { + socket.emit("disappearing_text", { message: text, x: x, y: y, id: entity.id, args: d_args }); + } // volatile. } -function magiport_someone(pulled,player) -{ - var spot=random_one([[-10,16],[10,16],[10,-16],[-10,-16],[0,-24],[-20,-32],[20,32],[-20,32],[20,-32]]); - var spot=safe_xy_nearby(player.map,player.x+spot[0],player.y+spot[1]); - if(!spot) return false; - pulled.s.magiport={ms:400}; - pulled.s.magiport.x=spot.x; - pulled.s.magiport.y=spot.y; - pulled.s.magiport.f=player.name; - pulled.s.magiport.in=player.in; - pulled.s.magiport.map=player.map; - if(player.party==pulled.party) player.pdps+=2000; - resend(pulled,"u+cid") +function magiport_someone(pulled, player) { + var spot = random_one([ + [-10, 16], + [10, 16], + [10, -16], + [-10, -16], + [0, -24], + [-20, -32], + [20, 32], + [-20, 32], + [20, -32], + ]); + var spot = safe_xy_nearby(player.map, player.x + spot[0], player.y + spot[1]); + if (!spot) { + return false; + } + pulled.s.magiport = { ms: 400 }; + pulled.s.magiport.x = spot.x; + pulled.s.magiport.y = spot.y; + pulled.s.magiport.f = player.name; + pulled.s.magiport.in = player.in; + pulled.s.magiport.map = player.map; + if (player.party == pulled.party) { + player.pdps += 2000; + } + resend(pulled, "u+cid"); return true; } -function exchange(player,name,args) -{ - if(!args) args={}; - var socket=player.socket,done=false,total=0,current=0; - var table=D.drops[name]; - if(is_array(name)) - { - table=name; - name=args.name; +function exchange(player, name, args) { + if (!args) { + args = {}; } - if(name.startsWith("cosmo")) - { - table=clone(table); - table.forEach(function(drop){ - if(player.p.acx[drop[2]]) - drop[0]/=10**player.p.acx[drop[2]]; + var socket = player.socket; + var done = false; + var total = 0; + var current = 0; + var table = D.drops[name]; + if (is_array(name)) { + table = name; + name = args.name; + } + if (name.startsWith("cosmo")) { + table = clone(table); + table.forEach(function (drop) { + if (player.p.acx[drop[2]]) { + drop[0] /= 10 ** player.p.acx[drop[2]]; + } }); console.log(table); } - table.forEach(function(drop){ total+=drop[0]; }); - result=Math.random()*total; - table.forEach(function(drop){ - if(done) return; - current+=drop[0]; - if(result<=current) - { - done=true; - if(drop[1]=="gold") - { - player.gold+=drop[2]; - socket.emit("game_log",{message:"Received "+to_pretty_num(drop[2])+" gold",color:"gold"}); - if(drop[2]>3000000 && !player.stealth) broadcast("server_message",{"message":player.name+" received "+to_pretty_num(drop[2])+" gold","color":"gold"}); - } - else if(drop[1]=="shells") - { - add_shells(player,drop[2],name,true,"override"); - } - else if(drop[1]=="empty") - { - socket.emit("game_log","Didn't receive anything"); - } - else if(drop[1]=="cx" || drop[1]=="cxbundle") - { - player.p.acx[drop[2]]=(player.p.acx[drop[2]]||0)+1; - socket.emit("game_response",{response:"cx_new",acx:player.p.acx,name:drop[2],from:name,bundle:drop[1]=="cxbundle"}); - } - else if(drop[1]=="open") - { - exchange(player,drop[2]); - } - else - { - var item=create_new_item(drop[1],drop[2]),prop=undefined; - if(name=="glitch" && (G.items[item.name].upgrade || G.items[item.name].compound || character_slots.includes(G.items[item.name].type))) item.p="glitched"; - if(name=="glitch") args.phrase="Glitched"; - if(args.v) item.v=args.v; - if(drop[1]=="cxjar" || drop[1]=="emotionjar") item.data=drop[3]; - add_item(player,item,{r:1,phrase:args.phrase}); - socket.emit("game_log",{message:(args.phrase||"Received")+" "+item_to_phrase(item),color:colors.server_success}); + table.forEach(function (drop) { + total += drop[0]; + }); + result = Math.random() * total; + table.forEach(function (drop) { + if (done) { + return; + } + current += drop[0]; + if (result <= current) { + done = true; + if (drop[1] == "gold") { + player.gold += drop[2]; + socket.emit("game_log", { message: "Received " + to_pretty_num(drop[2]) + " gold", color: "gold" }); + if (drop[2] > 3000000 && !player.stealth) { + broadcast("server_message", { + message: player.name + " received " + to_pretty_num(drop[2]) + " gold", + color: "gold", + }); + } + } else if (drop[1] == "shells") { + add_shells(player, drop[2], name, true, "override"); + } else if (drop[1] == "empty") { + socket.emit("game_log", "Didn't receive anything"); + } else if (drop[1] == "cx" || drop[1] == "cxbundle") { + player.p.acx[drop[2]] = (player.p.acx[drop[2]] || 0) + 1; + socket.emit("game_response", { + response: "cx_new", + acx: player.p.acx, + name: drop[2], + from: name, + bundle: drop[1] == "cxbundle", + }); + } else if (drop[1] == "open") { + exchange(player, drop[2]); + } else { + var item = create_new_item(drop[1], drop[2]); + var prop = undefined; + if ( + name == "glitch" && + (G.items[item.name].upgrade || + G.items[item.name].compound || + character_slots.includes(G.items[item.name].type)) + ) { + item.p = "glitched"; + } + if (name == "glitch") { + args.phrase = "Glitched"; + } + if (args.v) { + item.v = args.v; + } + if (drop[1] == "cxjar" || drop[1] == "emotionjar") { + item.data = drop[3]; + } + add_item(player, item, { r: 1, phrase: args.phrase }); + socket.emit("game_log", { + message: (args.phrase || "Received") + " " + item_to_phrase(item), + color: colors.server_success, + }); } } }); - if(!done) socket.emit("game_log","Didn't receive anything"); + if (!done) { + socket.emit("game_log", "Didn't receive anything"); + } } -function chest_exchange(chest,name) -{ - var done=false,total=0,current=0; - D.drops[name].forEach(function(drop){ total+=drop[0]; }); - result=Math.random()*total; - D.drops[name].forEach(function(drop){ - if(done) return; - current+=drop[0]; - if(result<=current) - { - done=true; - if(drop[1]=="gold") - { - chest.gold+=drop[2]; - } - else if(drop[1]=="shells") - { - chest.cash+=drop[2]; - } - else if(drop[1]=="empty"); - else if(drop[1]=="open") - { - chest_exchange(chest,drop[2]); - } - else - { +function chest_exchange(chest, name) { + var done = false; + var total = 0; + var current = 0; + D.drops[name].forEach(function (drop) { + total += drop[0]; + }); + result = Math.random() * total; + D.drops[name].forEach(function (drop) { + if (done) { + return; + } + current += drop[0]; + if (result <= current) { + done = true; + if (drop[1] == "gold") { + chest.gold += drop[2]; + } else if (drop[1] == "shells") { + chest.cash += drop[2]; + } else if (drop[1] == "empty") { + } else if (drop[1] == "open") { + chest_exchange(chest, drop[2]); + } else { chest.items.push(create_new_item(drop[1])); } } }); } -var item_p_ignore={"grace":true,"giveaway":true,"gf":true,"price":true,"b":true,"rid":true,"list":true,"o":true,"oo":true,"src":true},item_trade_p_ignore={"grace":true,"o":true,"oo":true,"src":true}; +var item_p_ignore = { + grace: true, + giveaway: true, + gf: true, + price: true, + b: true, + rid: true, + list: true, + o: true, + oo: true, + src: true, +}; +var item_trade_p_ignore = { grace: true, o: true, oo: true, src: true }; // DOC // .name -> key from G.items @@ -2652,730 +3557,1142 @@ var item_p_ignore={"grace":true,"giveaway":true,"gf":true,"price":true,"b":true, // .giveaway // .list -> the list for .giveaway -function cache_item(current,trade,override) -{ - if(!current) return null; - var item={}; - if(trade) - { - for(var p in current) - if(!item_trade_p_ignore[p]) item[p]=current[p]; - if(!item.giveaway) delete item.list; +function cache_item(current, trade, override) { + if (!current) { + return null; } - else - { - for(var p in current) - if(!item_p_ignore[p]) item[p]=current[p]; + var item = {}; + if (trade) { + for (var p in current) { + if (!item_trade_p_ignore[p]) { + item[p] = current[p]; + } + } + if (!item.giveaway) { + delete item.list; + } + } else { + for (var p in current) { + if (!item_p_ignore[p]) { + item[p] = current[p]; + } + } + } + if (override) { + for (var p in override) { + item[p] = override[p]; + } } - if(override) for(var p in override) item[p]=override[p]; return item; } -function get_trade_slots(player) -{ - if(player.p.stand) - { - var num=16,slots=[]; - if(player.type=="merchant" && player.level>=80) num=30; - else if(player.type=="merchant" && (player.level>=70 || player.p.stand=="cstand")) num=24; - for(var i=1;i<=num;i++) slots.push("trade"+i); +function get_trade_slots(player) { + if (player.p.stand) { + var num = 16; + var slots = []; + if (player.type == "merchant" && player.level >= 80) { + num = 30; + } else if (player.type == "merchant" && (player.level >= 70 || player.p.stand == "cstand")) { + num = 24; + } + for (var i = 1; i <= num; i++) { + slots.push("trade" + i); + } return slots; + } else if (player.p.trades) { + return ["trade1", "trade2", "trade3", "trade4"]; } - else if(player.p.trades) return ["trade1","trade2","trade3","trade4"]; return []; } -function reslot_player(player) -{ - trade_slots.forEach(function(slot){try{ delete player.cslots[slot]; }catch(e){}}) - get_trade_slots(player).forEach(function(slot){ - player.cslots[slot]=cache_item(player.slots[slot],1); +function reslot_player(player) { + trade_slots.forEach(function (slot) { + try { + delete player.cslots[slot]; + } catch (e) {} + }); + get_trade_slots(player).forEach(function (slot) { + player.cslots[slot] = cache_item(player.slots[slot], 1); }); } -function cache_player_items(player) -{ +function cache_player_items(player) { // console.log("Cached "+player.name+"'s items"); - if(player.slots && player.slots.ring1 && player.slots.ring1.name=="tristone" && player.slots.ring1.level>=4) player.slots.ring1.name="darktristone"; - if(player.slots && player.slots.ring2 && player.slots.ring2.name=="tristone" && player.slots.ring2.level>=4) player.slots.ring2.name="darktristone"; - player.cslots={}; - player.citems=[]; - character_slots.forEach(function(slot){ - player.cslots[slot]=cache_item(player.slots[slot]); + if (player.slots && player.slots.ring1 && player.slots.ring1.name == "tristone" && player.slots.ring1.level >= 4) { + player.slots.ring1.name = "darktristone"; + } + if (player.slots && player.slots.ring2 && player.slots.ring2.name == "tristone" && player.slots.ring2.level >= 4) { + player.slots.ring2.name = "darktristone"; + } + player.cslots = {}; + player.citems = []; + character_slots.forEach(function (slot) { + player.cslots[slot] = cache_item(player.slots[slot]); }); - for(var i=0;i42) player.user[pack].length=42; +function init_bank_exit(player) { + for (var pack in bank_packs) { + if (!player.user[pack]) { + continue; + } + if (player.user[pack].length > 42) { + player.user[pack].length = 42; + } } } -function init_player(player) -{ - var class_def=G.classes[player.type]; - player.citems=[]; - if(player.slots.mainhand && player.slots.offhand && G.classes[player.type].doublehand[G.items[player.slots.mainhand.name].wtype||G.items[player.slots.mainhand.name].type] || player.slots.mainhand && !G.classes[player.type].mainhand[G.items[player.slots.mainhand.name].wtype||G.items[player.slots.mainhand.name].type] && !G.classes[player.type].doublehand[G.items[player.slots.mainhand.name].wtype||G.items[player.slots.mainhand.name].type]) - { - add_item(player,player.slots.mainhand,{announce:false}); - player.slots.mainhand=null; +function init_player(player) { + var class_def = G.classes[player.type]; + player.citems = []; + if ( + (player.slots.mainhand && + player.slots.offhand && + G.classes[player.type].doublehand[ + G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type + ]) || + (player.slots.mainhand && + !G.classes[player.type].mainhand[ + G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type + ] && + !G.classes[player.type].doublehand[ + G.items[player.slots.mainhand.name].wtype || G.items[player.slots.mainhand.name].type + ]) + ) { + add_item(player, player.slots.mainhand, { announce: false }); + player.slots.mainhand = null; } - if(player.slots.offhand && !G.classes[player.type].offhand[G.items[player.slots.offhand.name].wtype||G.items[player.slots.offhand.name].type]) - { - add_item(player,player.slots.offhand,{announce:false}); - player.slots.offhand=null; + if ( + player.slots.offhand && + !G.classes[player.type].offhand[G.items[player.slots.offhand.name].wtype || G.items[player.slots.offhand.name].type] + ) { + add_item(player, player.slots.offhand, { announce: false }); + player.slots.offhand = null; } - for(var i=0;i60) delete player.items[i].v; - if(player.items[i].expires) player.items[i].expires=new Date(player.items[i].expires); - if(!Object.keys(player.q||{}).length && player.items[i].name=="placeholder") player.items[i]=null; - } - check_slots.forEach(function(slot){ - if(player.slots[slot] && player.slots[slot].expires) player.slots[slot].expires=new Date(player.slots[slot].expires); - }); - if(player.p.item_num===undefined) player.p.item_num=parseInt(Math.random()*42) - for(var s in player.s||{}) - { - if(player.s[s] && player.s[s].last) player.s[s].last=new Date(player.s[s].last); - } - for(var dt in player.p.dt) - { - if(!is_string(player.p.dt[dt])) continue; - player.p.dt[dt]=new Date(player.p.dt[dt]); - } - for(var id in G.skills) - { - if(G.skills[id]['class'] && G.skills[id]['class'].includes(player.type) && G.skills[id].persistent) - { - player.last[id]=player.p.dt[id]||(new Date()); - if((G.skills[id].cooldown||G.skills[id].reuse_cooldown)>mssince(player.last[id])) - player.hitchhikers.push(["eval",{code:"skill_timeout('"+id+"',"+((G.skills[id].cooldown||G.skills[id].reuse_cooldown)-mssince(player.last[id]))+")"}]); + if (player.items[i].v && msince(new Date(player.items[i].v)) > 60) { + delete player.items[i].v; + } + if (player.items[i].expires) { + player.items[i].expires = new Date(player.items[i].expires); + } + if (!Object.keys(player.q || {}).length && player.items[i].name == "placeholder") { + player.items[i] = null; } } - if(!player.skin || !T[player.skin]) player.skin=class_def.looks[0][0],player.cx=clone(class_def.looks[0][1]); - prune_cx(player.cx||{}); - if(!player.p.acx) - { - player.p.acx={}; - player.p.xcx=[]; + check_slots.forEach(function (slot) { + if (player.slots[slot] && player.slots[slot].expires) { + player.slots[slot].expires = new Date(player.slots[slot].expires); + } + }); + if (player.p.item_num === undefined) { + player.p.item_num = parseInt(Math.random() * 42); + } + for (var s in player.s || {}) { + if (player.s[s] && player.s[s].last) { + player.s[s].last = new Date(player.s[s].last); + } + } + for (var dt in player.p.dt) { + if (!is_string(player.p.dt[dt])) { + continue; + } + player.p.dt[dt] = new Date(player.p.dt[dt]); + } + for (var id in G.skills) { + if (G.skills[id]["class"] && G.skills[id]["class"].includes(player.type) && G.skills[id].persistent) { + player.last[id] = player.p.dt[id] || new Date(); + if ((G.skills[id].cooldown || G.skills[id].reuse_cooldown) > mssince(player.last[id])) { + player.hitchhikers.push([ + "eval", + { + code: + "skill_timeout('" + + id + + "'," + + ((G.skills[id].cooldown || G.skills[id].reuse_cooldown) - mssince(player.last[id])) + + ")", + }, + ]); + } + } + } + if (!player.skin || !T[player.skin]) { + player.skin = class_def.looks[0][0]; + player.cx = clone(class_def.looks[0][1]); + } + prune_cx(player.cx || {}); + if (!player.p.acx) { + player.p.acx = {}; + player.p.xcx = []; + } + if (!player.p.emx) { + player.p.emx = {}; + } + if (!player.p.ap) { + player.p.ap = {}; + } // achievement progress + if (!player.p.achievements) { + player.p.achievements = {}; + } + if (player.p.mute) { + player.mute = true; + } + if (player.p.role == "gm") { + player.gm = true; + } + if (player.p.role) { + player.role = player.p.role; + } + if (events.holidayseason && !player.p.firstbuff) { + add_condition(player, "holidayspirit"); + player.p.firstbuff = true; + } + if (!player.p.ugrace || player.p.ugrace.length != 15) { + player.p.ugrace = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } // first digit and some of the last aren't used [01/10/17] + if (!player.p.cgrace || player.p.cgrace.length != 15) { + player.p.cgrace = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + if (!player.p.ograce) { + player.p.ograce = 0; + } + if (!player.p.minutes) { + player.p.minutes = 0; + } + if (!player.p.stats) { + player.p.stats = { monsters: {} }; + } + if (!player.p.stats.exchanges) { + player.p.stats.exchanges = {}; + } + if (!player.p.stats.monsters_diff) { + player.p.stats.monsters_diff = {}; + } + if (is_array(player.cx)) { + player.cx = {}; + } + if (player.items.length < 42) { + player.items.length = 42; } - if(!player.p.emx) player.p.emx={}; - if(!player.p.ap) player.p.ap={}; // achievement progress - if(!player.p.achievements) player.p.achievements={}; - if(player.p.mute) player.mute=true; - if(player.p.role=="gm") player.gm=true; - if(player.p.role) player.role=player.p.role; - if(events.holidayseason && !player.p.firstbuff) add_condition(player,"holidayspirit"),player.p.firstbuff=true; - if(!player.p.ugrace || player.p.ugrace.length!=15) player.p.ugrace=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; // first digit and some of the last aren't used [01/10/17] - if(!player.p.cgrace || player.p.cgrace.length!=15) player.p.cgrace=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; - if(!player.p.ograce) player.p.ograce=0; - if(!player.p.minutes) player.p.minutes=0; - if(!player.p.stats) player.p.stats={"monsters":{}}; - if(!player.p.stats.exchanges) player.p.stats.exchanges={}; - if(!player.p.stats.monsters_diff) player.p.stats.monsters_diff={}; - if(is_array(player.cx)) player.cx={}; - if(player.items.length<42) player.items.length=42; //if(player.s.monsterhunt && player.s.monsterhunt.sn!=region+" "+server_name) delete player.s.monsterhunt; - for(var n in player.s) - { - if(to_number(player.s[n])) player.s[n]={ms:to_number(player.s[n])}; + for (var n in player.s) { + if (to_number(player.s[n])) { + player.s[n] = { ms: to_number(player.s[n]) }; + } } - player.xyh=[{x:player.x,y:player.y,map:player.map,t:new Date()}]; // xy history - player.rid=round(Math.random()*1000); // random number to randomise events + player.xyh = [{ x: player.x, y: player.y, map: player.map, t: new Date() }]; // xy history + player.rid = round(Math.random() * 1000); // random number to randomise events } -function init_player_exit(player) // these are safety routines, if a bug allows the inventory to grow infinitely, this prevents item duplication possibilities [18/10/18] -{ - for(var id in player.q) - player.q[id].stale=true; - if(player.items.length>70) player.items.length=70; - if(Object.keys(player.slots).length>500) player.slots={}; - for(var id in G.skills) - { - if(G.skills[id]['class'] && G.skills[id]['class'].includes(player.type) && G.skills[id].persistent && player.last[id]) - { - player.p.dt[id]=player.last[id]; +function init_player_exit(player) { + // these are safety routines, if a bug allows the inventory to grow infinitely, this prevents item duplication possibilities [18/10/18] + for (var id in player.q) { + player.q[id].stale = true; + } + if (player.items.length > 70) { + player.items.length = 70; + } + if (Object.keys(player.slots).length > 500) { + player.slots = {}; + } + for (var id in G.skills) { + if ( + G.skills[id]["class"] && + G.skills[id]["class"].includes(player.type) && + G.skills[id].persistent && + player.last[id] + ) { + player.p.dt[id] = player.last[id]; } } } -function push_xyh(player,x,y) -{ - if(!player.xyh) return; - var last=player.xyh[player.xyh.length-1],current={x:x,y:y,map:player.map,t:new Date()}; - if(mssince(last.t)>50 || simple_distance(last,current)>1) +function push_xyh(player, x, y) { + if (!player.xyh) { + return; + } + var last = player.xyh[player.xyh.length - 1]; + var current = { x: x, y: y, map: player.map, t: new Date() }; + if (mssince(last.t) > 50 || simple_distance(last, current) > 1) { player.xyh.push(current); - if(player.xyh.length>100) player.xyh.shift(); + } + if (player.xyh.length > 100) { + player.xyh.shift(); + } } -function item_name(item) -{ - var def=G.items[item.name],name=def.name; - if(item.level) name+=" +"+item.level; +function item_name(item) { + var def = G.items[item.name]; + var name = def.name; + if (item.level) { + name += " +" + item.level; + } return name; } -function startswith_an(name) -{ - if(in_arr(name.toLowerCase()[0],["a","e","o","u","i"])) return true; +function startswith_an(name) { + if (in_arr(name.toLowerCase()[0], ["a", "e", "o", "u", "i"])) { + return true; + } return false; } -function item_to_phrase(item) -{ - if(item.q && item.q>1) - return G.items[item.name].name+" [x"+item.q+"]"; - var prefix=""; - if(startswith_an(G.items[item.name].name)) prefix="an " - else prefix="a "; - if(item.p) prefix+=item.p.toTitleCase()+" "; - if(item.level) return prefix+G.items[item.name].name+" +"+item.level; - return prefix+G.items[item.name].name; +function item_to_phrase(item) { + if (item.q && item.q > 1) { + return G.items[item.name].name + " [x" + item.q + "]"; + } + var prefix = ""; + if (startswith_an(G.items[item.name].name)) { + prefix = "an "; + } else { + prefix = "a "; + } + if (item.p) { + prefix += item.p.toTitleCase() + " "; + } + if (item.level) { + return prefix + G.items[item.name].name + " +" + item.level; + } + return prefix + G.items[item.name].name; } -function killed_message(type) -{ - if(G.monsters[type].prefix==="the") return "killed the "+G.monsters[type].name; - else if(G.monsters[type].prefix==="") return "killed "+G.monsters[type].name; - else if(startswith_an(G.monsters[type].name)) return "killed an "+G.monsters[type].name; - else return "killed a "+G.monsters[type].name; +function killed_message(type) { + if (G.monsters[type].prefix === "the") { + return "killed the " + G.monsters[type].name; + } else if (G.monsters[type].prefix === "") { + return "killed " + G.monsters[type].name; + } else if (startswith_an(G.monsters[type].name)) { + return "killed an " + G.monsters[type].name; + } else { + return "killed a " + G.monsters[type].name; + } } -function is_xy_safe_old(map,x,y) -{ - var safe=true; - if(smap_data[map]==-1) return true; - [[0,0],[0,smap_step+1],[0,-smap_step-1],[smap_step+1,0],[-smap_step-1,0],[0,smap_step/2],[smap_step/2,0],[0,-smap_step/2],[-smap_step/2,0]].forEach(function(m){ - var data=smap_data[map][phash(x+m[0],y+m[1])]; - if(data || data===undefined) safe=false; +function is_xy_safe_old(map, x, y) { + var safe = true; + if (smap_data[map] == -1) { + return true; + } + [ + [0, 0], + [0, smap_step + 1], + [0, -smap_step - 1], + [smap_step + 1, 0], + [-smap_step - 1, 0], + [0, smap_step / 2], + [smap_step / 2, 0], + [0, -smap_step / 2], + [-smap_step / 2, 0], + ].forEach(function (m) { + var data = smap_data[map][phash(x + m[0], y + m[1])]; + if (data || data === undefined) { + safe = false; + } }); return safe; } -function is_xy_safe(map,x,y) -{ - var safe=true; - if(smap_data[map]==-1) return true; +function is_xy_safe(map, x, y) { + var safe = true; + if (smap_data[map] == -1) { + return true; + } // x=parseInt(x)-(parseInt(x)%smap_step); // y=parseInt(y)-(parseInt(y)%smap_step); - [[0,0],[0,1],[0,-1],[1,0],[-1,0],[1,1],[-1,1],[1,-1],[-1,-1]].forEach(function(m){ // ,[2,2],[-2,-2],[2,-2],[-2,2] - var data=smap_data[map][phash(x+m[0]*smap_step,y+m[1]*smap_step)]; - if(data || data===undefined) safe=false; + [ + [0, 0], + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + [1, 1], + [-1, 1], + [1, -1], + [-1, -1], + ].forEach(function (m) { + // ,[2,2],[-2,-2],[2,-2],[-2,2] + var data = smap_data[map][phash(x + m[0] * smap_step, y + m[1] * smap_step)]; + if (data || data === undefined) { + safe = false; + } }); return safe; } -function safe_xy_nearby(map,x,y) -{ - var point=false; +function safe_xy_nearby(map, x, y) { + var point = false; // if(is_xy_safe(map,x,y)) return {x:x,y:y}; - x=smap_round(x); y=smap_round(y); + x = smap_round(x); + y = smap_round(y); // [1,0],[0,1],[1,1],[-1,1],[1,-1],[-1,0],[0,-1],[-1,-1],[2,0],[0,2],[2,2],[-2,-2],[-2,0],[0,-2] - [[0,0],[1,0],[0,1],[-1,0],[0,-1],[2,0],[0,2],[-2,0],[0,-2],[3,0],[0,3],[-3,0],[0,-3],].forEach(function(m){ - if(!point && is_xy_safe(map,x+m[0]*smap_step,y+m[1]*smap_step)) point={x:x+m[0]*smap_step,y:y+m[1]*smap_step}; + [ + [0, 0], + [1, 0], + [0, 1], + [-1, 0], + [0, -1], + [2, 0], + [0, 2], + [-2, 0], + [0, -2], + [3, 0], + [0, 3], + [-3, 0], + [0, -3], + ].forEach(function (m) { + if (!point && is_xy_safe(map, x + m[0] * smap_step, y + m[1] * smap_step)) { + point = { x: x + m[0] * smap_step, y: y + m[1] * smap_step }; + } }); return point; - } -function smap_round(x) // for blink, port, magiport -{ - var x1=parseInt(x)-(parseInt(x)%smap_step); - var x2=x1+smap_step; - if(x<0) x2=x1-smap_step; - if(abs(x-x1)50000) - { - smap_data[map]=-1; - server_log(map+" is either un-bounded or the spawn point is too close to an edge",1); + while (start < queue.length) { + if (queue.length > 50000) { + smap_data[map] = -1; + server_log(map + " is either un-bounded or the spawn point is too close to an edge", 1); return; } - var current=queue[start++]; - [[0,smap_step],[0,-smap_step],[smap_step,0],[-smap_step,0]].forEach(function(m){ - if(level==0) - { - if(can_move({map:map,x:current[0],y:current[1],going_x:current[0]+m[0],going_y:current[1]+m[1]})) push(current[0]+m[0],current[1]+m[1],level); - else push(current[0]+m[0],current[1]+m[1],level+1); - } - else - { - push(current[0]+m[0],current[1]+m[1],level+1); + var current = queue[start++]; + [ + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + if (level == 0) { + if ( + can_move({ map: map, x: current[0], y: current[1], going_x: current[0] + m[0], going_y: current[1] + m[1] }) + ) { + push(current[0] + m[0], current[1] + m[1], level); + } else { + push(current[0] + m[0], current[1] + m[1], level + 1); + } + } else { + push(current[0] + m[0], current[1] + m[1], level + 1); } }); } - if(level==0 && 0) // In the new system, 1 is safe too [01/08/18] - { - for(var i=0;i<(G.maps[map].data.x_lines||[]).length;i++) - { - var current=G.maps[map].data.x_lines[i]; - push(current[0],current[1],0); push(current[0],current[2],0); - for(var j=current[1];j120000) - { - amap_data[map]={}; - server_log(map+" is either un-bounded or the spawn point is too close to an edge [server_bfs2]",1); + x = parseInt(x) - (parseInt(x) % amap_step); + y = parseInt(y) - (parseInt(y) % amap_step); + return x + "|" + y; +} + +function rphash2(x, y) { + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = amap_round(x); + y = amap_round(y); + return x + "|" + y; +} + +var amap_data = {}; +var amap_step = 8; +function server_bfs2(map) { + var base = { h: 9, v: 9, vn: 2 }; + var xmult = 1; + var vhmult = 1; + amap_data[map] = {}; //new Map(); - Map can't be sent to the Worker! :) [19/08/20] + var queue = []; + var visited = {}; + var start = 0; + var level = 0; + var chiding_places = []; + function push(x, y, c) { + x = parseInt(x) - (parseInt(x) % amap_step); + y = parseInt(y) - (parseInt(y) % amap_step); + var hash = phash2(x, y); + if (visited[hash]) { return; } - var current=queue[start++]; - [[amap_step,amap_step],[amap_step,-amap_step],[-amap_step,-amap_step],[-amap_step,amap_step],[0,amap_step],[0,-amap_step],[amap_step,0],[-amap_step,0]].forEach(function(m){ - if(can_move({map:map,x:current[0],y:current[1],going_x:current[0]+m[0]*xmult,going_y:current[1]+m[1]*xmult,base:base})) - push(current[0]+m[0],current[1]+m[1]); + queue.push([x, y]); + visited[hash] = 1; + } + G.maps[map].spawns.forEach(function (s) { + var x = s[0]; + var y = s[1]; + var done = false; + x = parseInt(x) - (parseInt(x) % amap_step); + y = parseInt(y) - (parseInt(y) % amap_step); + var current = [x, y]; + [ + [0, 0], + [amap_step, amap_step], + [amap_step, -amap_step], + [-amap_step, -amap_step], + [-amap_step, amap_step], + [0, amap_step], + [0, -amap_step], + [amap_step, 0], + [-amap_step, 0], + ].forEach(function (m) { + if ( + !done && + can_move({ + map: map, + x: x, + y: y, + going_x: current[0] + m[0] * xmult, + going_y: current[1] + m[1] * xmult, + base: base, + }) + ) { + push(current[0] + m[0], current[1] + m[1], level); + done = true; + } + }); + }); + while (start < queue.length) { + if (queue.length > 120000) { + amap_data[map] = {}; + server_log(map + " is either un-bounded or the spawn point is too close to an edge [server_bfs2]", 1); + return; + } + var current = queue[start++]; + [ + [amap_step, amap_step], + [amap_step, -amap_step], + [-amap_step, -amap_step], + [-amap_step, amap_step], + [0, amap_step], + [0, -amap_step], + [amap_step, 0], + [-amap_step, 0], + ].forEach(function (m) { + if ( + can_move({ + map: map, + x: current[0], + y: current[1], + going_x: current[0] + m[0] * xmult, + going_y: current[1] + m[1] * xmult, + base: base, + }) + ) { + push(current[0] + m[0], current[1] + m[1]); + } }); } - for(var hash in visited) - { - var x=parseInt(hash.split("|")[0]); - var y=parseInt(hash.split("|")[1]); - var count=0; - [[amap_step,amap_step],[amap_step,-amap_step],[-amap_step,-amap_step],[-amap_step,amap_step],[0,amap_step],[0,-amap_step],[amap_step,0],[-amap_step,0]].forEach(function(m){ - if(visited[(x+m[0])+"|"+(y+m[1])]) count+=1; + for (var hash in visited) { + var x = parseInt(hash.split("|")[0]); + var y = parseInt(hash.split("|")[1]); + var count = 0; + [ + [amap_step, amap_step], + [amap_step, -amap_step], + [-amap_step, -amap_step], + [-amap_step, amap_step], + [0, amap_step], + [0, -amap_step], + [amap_step, 0], + [-amap_step, 0], + ].forEach(function (m) { + if (visited[x + m[0] + "|" + (y + m[1])]) { + count += 1; + } }); - if(count>=2) - { - amap_data[map][hash]=1; + if (count >= 2) { + amap_data[map][hash] = 1; } } - for(var hash in amap_data[map]) - { - var x=parseInt(hash.split("|")[0]); - var y=parseInt(hash.split("|")[1]); - var count=0; - [[amap_step,amap_step],[amap_step,-amap_step],[-amap_step,-amap_step],[-amap_step,amap_step],[0,amap_step],[0,-amap_step],[amap_step,0],[-amap_step,0]].forEach(function(m){ - if(amap_data[map][(x+m[0])+"|"+(y+m[1])]) count+=1; + for (var hash in amap_data[map]) { + var x = parseInt(hash.split("|")[0]); + var y = parseInt(hash.split("|")[1]); + var count = 0; + [ + [amap_step, amap_step], + [amap_step, -amap_step], + [-amap_step, -amap_step], + [-amap_step, amap_step], + [0, amap_step], + [0, -amap_step], + [amap_step, 0], + [-amap_step, 0], + ].forEach(function (m) { + if (amap_data[map][x + m[0] + "|" + (y + m[1])]) { + count += 1; + } }); - amap_data[map][hash]=count; + amap_data[map][hash] = count; } } -function can_amove(map,sx,sy,tx,ty) -{ - if(!amap_data[map][amap_round(tx)+"|"+amap_round(ty)]) return false; - - if(sy==ty) - { - var p=tx1200 || total>4000) - { - server_log(["heap","fast_astar"]); - if(theone) - { - var result=finalise(theone); + while (heap.array.length) { + if (heap.array.length > 1200 || total > 4000) { + server_log(["heap", "fast_astar"]); + if (theone) { + var result = finalise(theone); result.push("bad"); return result; } return null; } - var current=heap.removeTop(),dist=point_distance(current.x,current.y,tx,ty),rnd=Math.random()*100; + var current = heap.removeTop(); + var dist = point_distance(current.x, current.y, tx, ty); + var rnd = Math.random() * 100; // console.log([current.x,current.y,visited[current.x+"|"+current.y]]); - if(dist<28) - { - good=true; + if (dist < 28) { + good = true; return finalise(current); } - if(dist+current.bad-rnd1200 || last>4000) - { - server_log(["queue","fast_abfs"]); - monster.bpath=(monster.bpath||0)+1; - if(theone && monster.bpath<20) - return finalise(theone); + if (!good && point_distance(sx, sy, dx, dy) < 30) { return null; } - var current=queue[start++],dist=point_distance(current.x,current.y,tx,ty),rnd=Math.random()*100; - if(dist<28) - { - monster.bpath=0; good=true; - return finalise(current); + server_log([last, dx, dy, mssince(start_t)]); + return [dx, dy]; + } + for (var step = 1; step <= 2; step++) { + if (last) { + break; } - if(dist+current.bad-rnd 1200 || last > 4000) { + server_log(["queue", "fast_abfs"]); + monster.bpath = (monster.bpath || 0) + 1; + if (theone && monster.bpath < 20) { + return finalise(theone); + } + return null; + } + var current = queue[start++]; + var dist = point_distance(current.x, current.y, tx, ty); + var rnd = Math.random() * 100; + if (dist < 28) { + monster.bpath = 0; + good = true; + return finalise(current); + } + if (dist + current.bad - rnd < best) { + best = dist + current.bad - rnd; + theone = current; + } + [ + [amap_step, amap_step, 0, amap_step * 1.41], + [amap_step, -amap_step, 1, amap_step * 1.41], + [-amap_step, -amap_step, 2, amap_step * 1.41], + [-amap_step, amap_step, 3, amap_step * 1.41], + [0, amap_step, 4, amap_step], + [0, -amap_step, 5, amap_step], + [amap_step, 0, 6, amap_step], + [-amap_step, 0, 7, amap_step], + ].forEach(function (m) { + var cx = current.x + m[0]; + var cy = current.y + m[1]; + if (amap_data[map][cx + "|" + cy] && !visited[cx + "|" + cy]) { + hpush(cx, cy, current.x + "|" + current.y, m[2], current.bad + m[3] + ((current.dir != m[2] && 0.5) || 0)); + } + }); + } + server_log(["no start", "fast_abfs"]); } -function add_call_cost(socket,num,method) -{ - if(!socket) socket=1; - if(is_number(socket)) num=socket,socket=current_socket; - if(socket.socket) socket=socket.socket; // player - if(!num) num=1; - if(!method) method=ls_method; +function add_call_cost(socket, num, method) { + if (!socket) { + socket = 1; + } + if (is_number(socket)) { + num = socket; + socket = current_socket; + } + if (socket.socket) { + socket = socket.socket; + } // player + if (!num) { + num = 1; + } + if (!method) { + method = ls_method; + } + while (socket.calls.length && mssince(socket.calls[0][0]) > 4000) { + socket.calls.shift(); + } // #TODO: make one operation [27/02/23] - while(socket.calls.length && mssince(socket.calls[0][0])>4000) socket.calls.shift(); // #TODO: make one operation [27/02/23] - - if(socket.calls.length && socket.calls[socket.calls.length-1][1]==method && num!=-1) - socket.calls[socket.calls.length-1][2]+=num; - else - socket.calls.push([new Date(),method,num==-1?1:num]); -} - -function reduce_call_cost(socket,num) -{ - if(!socket) socket=1; - if(is_number(socket)) num=socket,socket=current_socket; - if(socket.socket) socket=socket.socket; // player - if(!num) num=1; - - if(socket.calls.length && socket.calls[socket.calls.length-1][1]==ls_method) - { - socket.calls[socket.calls.length-1][2]-=num; - if(socket.calls[socket.calls.length-1][2]<=0) - socket.calls.pop(); + if (socket.calls.length && socket.calls[socket.calls.length - 1][1] == method && num != -1) { + socket.calls[socket.calls.length - 1][2] += num; + } else { + socket.calls.push([new Date(), method, num == -1 ? 1 : num]); } } -function get_call_cost(socket) -{ - if(!socket) socket=current_socket; - if(socket.socket) socket=socket.socket; // player +function reduce_call_cost(socket, num) { + if (!socket) { + socket = 1; + } + if (is_number(socket)) { + num = socket; + socket = current_socket; + } + if (socket.socket) { + socket = socket.socket; + } // player + if (!num) { + num = 1; + } - while(socket.calls.length && mssince(socket.calls[0][0])>4000) socket.calls.shift(); // #TODO: make one operation [27/02/23] + if (socket.calls.length && socket.calls[socket.calls.length - 1][1] == ls_method) { + socket.calls[socket.calls.length - 1][2] -= num; + if (socket.calls[socket.calls.length - 1][2] <= 0) { + socket.calls.pop(); + } + } +} - var cost=0; - socket.calls.forEach(function(c){cost+=c[2];}); +function get_call_cost(socket) { + if (!socket) { + socket = current_socket; + } + if (socket.socket) { + socket = socket.socket; + } // player + + while (socket.calls.length && mssince(socket.calls[0][0]) > 4000) { + socket.calls.shift(); + } // #TODO: make one operation [27/02/23] + + var cost = 0; + socket.calls.forEach(function (c) { + cost += c[2]; + }); return cost; } - -function set_direction(){} // compatibility +function set_direction() {} // compatibility function symmetricDecrypt(input, key, checkHmac) { - var aesIv = crypto.createDecipheriv('aes-256-ecb', key, ''); + var aesIv = crypto.createDecipheriv("aes-256-ecb", key, ""); aesIv.setAutoPadding(false); aesIv.end(input.slice(0, 16)); var iv = aesIv.read(); - var aesData = crypto.createDecipheriv('aes-256-cbc', key, iv); + var aesData = crypto.createDecipheriv("aes-256-cbc", key, iv); aesData.end(input.slice(16)); var plaintext = aesData.read(); @@ -3392,7 +4709,7 @@ function symmetricDecrypt(input, key, checkHmac) { } return plaintext; -}; +} function parseAppTicket(ticket) { // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl @@ -3438,11 +4755,16 @@ function parseAppTicket(ticket) { // Start reading the ownership ticket let ownershipTicketOffset = ticket.offset; let ownershipTicketLength = ticket.readUint32(); // including itself, for some reason - if (ownershipTicketOffset + ownershipTicketLength != ticket.limit && ownershipTicketOffset + ownershipTicketLength + 128 != ticket.limit) { + if ( + ownershipTicketOffset + ownershipTicketLength != ticket.limit && + ownershipTicketOffset + ownershipTicketLength + 128 != ticket.limit + ) { return null; } - let i, j, dlc; + let i; + let j; + let dlc; details.version = ticket.readUint32(); details.steamID = ticket.readUint64().toString(); @@ -3485,27 +4807,40 @@ function parseAppTicket(ticket) { let date = new Date(); details.isExpired = details.ownershipTicketExpires < date; - details.hasValidSignature = !!details.signature && SteamCrypto.verifySignature(ticket.slice(ownershipTicketOffset, ownershipTicketOffset + ownershipTicketLength).toBuffer(), details.signature); + details.hasValidSignature = + !!details.signature && + SteamCrypto.verifySignature( + ticket.slice(ownershipTicketOffset, ownershipTicketOffset + ownershipTicketLength).toBuffer(), + details.signature, + ); details.isValid = !details.isExpired && (!details.signature || details.hasValidSignature); } catch (ex) { - console.log("parseAppTicket: "+ex); + console.log("parseAppTicket: " + ex); return details; return null; // not a valid ticket } return details; +} + +var proto = { + nested: { + EncryptedAppTicket: { + fields: { + ticketVersionNo: { type: "uint32", id: 1 }, + crcEncryptedticket: { type: "uint32", id: 2 }, + cbEncrypteduserdata: { type: "uint32", id: 3 }, + cbEncryptedAppownershipticket: { type: "uint32", id: 4 }, + encryptedTicket: { type: "bytes", id: 5 }, + }, + }, + }, }; +var proto_root = protobuf.Root.fromJSON(proto); +var EncryptedAppTicket = proto_root.lookupType("EncryptedAppTicket"); -var proto={ "nested": {EncryptedAppTicket: { fields: - { ticketVersionNo: { type: 'uint32', id: 1 }, - crcEncryptedticket: { type: 'uint32', id: 2 }, - cbEncrypteduserdata: { type: 'uint32', id: 3 }, - cbEncryptedAppownershipticket: { type: 'uint32', id: 4 }, - encryptedTicket: { type: 'bytes', id: 5 } } }}}; -var proto_root=protobuf.Root.fromJSON(proto); -var EncryptedAppTicket=proto_root.lookupType("EncryptedAppTicket"); - -function getClientIp(req) { //https://github.com/pbojinov/request-ip/blob/master/index.js +function getClientIp(req) { + //https://github.com/pbojinov/request-ip/blob/master/index.js // NOTE: Back in the day, we had a system called ipass - for some weird and unexplainable reason, no Node server, mainly Socket.io, can track the current IP of a client // So the clients needed to ping the game every 40 seconds, at a secondary, http-only handler just to get their IP - and, again, for some extremely weird reason, Node servers, don't let you just easily disconnect a client // Rather than auto Keep-Alive, so it had a hacky disconnect routine - all in all, decided to move this system to App Engine, while the SSL refactoring was happening [17/11/18] @@ -3515,20 +4850,21 @@ function getClientIp(req) { //https://github.com/pbojinov/request-ip/blob/master // workaround to get real client IP // most likely because our app will be behind a [reverse] proxy or load balancer - var clientIp = req.headers['x-client-ip']; - var forwardedForAlt = req.headers['x-forwarded-for']; - var realIp = req.headers['x-real-ip']; - + var clientIp = req.headers["x-client-ip"]; + var forwardedForAlt = req.headers["x-forwarded-for"]; + var realIp = req.headers["x-real-ip"]; + // more obsure ones below - var clusterClientIp = req.headers['x-cluster-client-ip']; - var forwardedAlt = req.headers['x-forwarded']; - var forwardedFor = req.headers['forwarded-for']; - var forwarded = req.headers['forwarded']; - + var clusterClientIp = req.headers["x-cluster-client-ip"]; + var forwardedAlt = req.headers["x-forwarded"]; + var forwardedFor = req.headers["forwarded-for"]; + var forwarded = req.headers["forwarded"]; + // remote address check var reqConnectionRemoteAddress = req.connection ? req.connection.remoteAddress : null; var reqSocketRemoteAddress = req.socket ? req.socket.remoteAddress : null; - var reqConnectionSocketRemoteAddress = (req.connection && req.connection.socket) ? req.connection.socket.remoteAddress : null; + var reqConnectionSocketRemoteAddress = + req.connection && req.connection.socket ? req.connection.socket.remoteAddress : null; var reqInfoRemoteAddress = req.info ? req.info.remoteAddress : null; // x-client-ip @@ -3539,23 +4875,23 @@ function getClientIp(req) { //https://github.com/pbojinov/request-ip/blob/master // x-forwarded-for // (typically when your node app is behind a load-balancer (eg. AWS ELB) or proxy) else if (forwardedForAlt) { - // x-forwarded-for may return multiple IP addresses in the format: - // "client IP, proxy 1 IP, proxy 2 IP" - // Therefore, the right-most IP address is the IP address of the most recent proxy + // x-forwarded-for may return multiple IP addresses in the format: + // "client IP, proxy 1 IP, proxy 2 IP" + // Therefore, the right-most IP address is the IP address of the most recent proxy // and the left-most IP address is the IP address of the originating client. // source: http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html - var forwardedIps = forwardedForAlt.split(','); + var forwardedIps = forwardedForAlt.split(","); ipAddress = forwardedIps[0]; } - // x-real-ip + // x-real-ip // (default nginx proxy/fcgi) else if (realIp) { // alternative to x-forwarded-for, used by some proxies ipAddress = realIp; } - // x-cluster-client-ip + // x-cluster-client-ip // (Rackspace LB and Riverbed's Stingray) // http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address // https://splash.riverbed.com/docs/DOC-1926 @@ -3581,84 +4917,133 @@ function getClientIp(req) { //https://github.com/pbojinov/request-ip/blob/master // remote address checks else if (reqConnectionRemoteAddress) { ipAddress = reqConnectionRemoteAddress; + } else if (reqSocketRemoteAddress) { + ipAddress = reqSocketRemoteAddress; + } else if (reqConnectionSocketRemoteAddress) { + ipAddress = reqConnectionSocketRemoteAddress; + } else if (reqInfoRemoteAddress) { + ipAddress = reqInfoRemoteAddress; } - else if (reqSocketRemoteAddress) { - ipAddress = reqSocketRemoteAddress - } - else if (reqConnectionSocketRemoteAddress) { - ipAddress = reqConnectionSocketRemoteAddress - } - else if (reqInfoRemoteAddress) { - ipAddress = reqInfoRemoteAddress - } // return null if we cannot find an address else { ipAddress = null; } - + return ipAddress; } -function deduct_gender(player) -{ - if(player.cx && player.cx.head && player.cx.head[0]=="f") return "female"; +function deduct_gender(player) { + if (player.cx && player.cx.head && player.cx.head[0] == "f") { + return "female"; + } return "male"; } -function safe_search(obj,phrase) -{ - obj=JSON.parse(safe_stringify(obj)); - for(l1 in obj) - { - var c1=obj[l1]; - if(c1 && is_string(c1) && c1.indexOf(phrase)!=-1 || l1.indexOf(phrase)!=-1) console.log(l1+" "); - if(!is_object(c1)) continue; - for(l2 in c1) - { - var c2=c1[l2]; - if(c2 && is_string(c2) && c2.indexOf(phrase)!=-1 || l2.indexOf(phrase)!=-1) console.log(l1+" "+l2); - if(!is_object(c2)) continue; - for(l3 in c2) - { - var c3=c2[l3]; - if(c3 && is_string(c3) && c3.indexOf(phrase)!=-1 || l3.indexOf(phrase)!=-1) console.log(l1+" "+l2+" "+l3); - if(!is_object(c3)) continue; - for(l4 in c3) - { - var c4=c3[l4]; - if(c4 && is_string(c4) && c4.indexOf(phrase)!=-1 || l4.indexOf(phrase)!=-1) console.log(l1+" "+l2+" "+l3+" "+l4); - if(!is_object(c4)) continue; - for(l5 in c4) - { - var c5=c4[l5]; - if(c5 && is_string(c5) && c5.indexOf(phrase)!=-1 || l5.indexOf(phrase)!=-1) console.log(l1+" "+l2+" "+l3+" "+l4+" "+l5); - if(!is_object(c5)) continue; - for(l6 in c5) - { - var c6=c5[l6]; - if(c6 && is_string(c6) && c6.indexOf(phrase)!=-1 || l6.indexOf(phrase)!=-1) console.log(l1+" "+l2+" "+l3+" "+l4+" "+l5+" "+l6); - if(!is_object(c6)) continue; - for(l7 in c6) - { - var c7=c6[l7]; - if(c7 && is_string(c7) && c7.indexOf(phrase)!=-1 || l7.indexOf(phrase)!=-1) console.log(l1+" "+l2+" "+l3+" "+l4+" "+l5+" "+l6+" "+l7); - if(!is_object(c7)) continue; - for(l8 in c7) - { - var c8=c7[l8]; - if(c8 && is_string(c8) && c8.indexOf(phrase)!=-1 || l8.indexOf(phrase)!=-1) console.log(l1+" "+l2+" "+l3+" "+l4+" "+l5+" "+l6+" "+l7+" "+l8); - if(!is_object(c8)) continue; - for(l9 in c8) - { - var c9=c8[l9]; - if(c9 && is_string(c9) && c9.indexOf(phrase)!=-1 || l9.indexOf(phrase)!=-1) console.log(l1+" "+l2+" "+l3+" "+l4+" "+l5+" "+l6+" "+l7+" "+l8+" "+l9); - if(!is_object(c9)) continue; - for(l10 in c9) - { - var c10=c9[l10]; - if(c10 && is_string(c10) && c10.indexOf(phrase)!=-1 || l10.indexOf(phrase)!=-1) console.log(l1+" "+l2+" "+l3+" "+l4+" "+l5+" "+l6+" "+l7+" "+l8+" "+l9+" "+l10); - if(!is_object(c10)) continue; +function safe_search(obj, phrase) { + obj = JSON.parse(safe_stringify(obj)); + for (l1 in obj) { + var c1 = obj[l1]; + if ((c1 && is_string(c1) && c1.indexOf(phrase) != -1) || l1.indexOf(phrase) != -1) { + console.log(l1 + " "); + } + if (!is_object(c1)) { + continue; + } + for (l2 in c1) { + var c2 = c1[l2]; + if ((c2 && is_string(c2) && c2.indexOf(phrase) != -1) || l2.indexOf(phrase) != -1) { + console.log(l1 + " " + l2); + } + if (!is_object(c2)) { + continue; + } + for (l3 in c2) { + var c3 = c2[l3]; + if ((c3 && is_string(c3) && c3.indexOf(phrase) != -1) || l3.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3); + } + if (!is_object(c3)) { + continue; + } + for (l4 in c3) { + var c4 = c3[l4]; + if ((c4 && is_string(c4) && c4.indexOf(phrase) != -1) || l4.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4); + } + if (!is_object(c4)) { + continue; + } + for (l5 in c4) { + var c5 = c4[l5]; + if ((c5 && is_string(c5) && c5.indexOf(phrase) != -1) || l5.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5); + } + if (!is_object(c5)) { + continue; + } + for (l6 in c5) { + var c6 = c5[l6]; + if ((c6 && is_string(c6) && c6.indexOf(phrase) != -1) || l6.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6); + } + if (!is_object(c6)) { + continue; + } + for (l7 in c6) { + var c7 = c6[l7]; + if ((c7 && is_string(c7) && c7.indexOf(phrase) != -1) || l7.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7); + } + if (!is_object(c7)) { + continue; + } + for (l8 in c7) { + var c8 = c7[l8]; + if ((c8 && is_string(c8) && c8.indexOf(phrase) != -1) || l8.indexOf(phrase) != -1) { + console.log(l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7 + " " + l8); + } + if (!is_object(c8)) { + continue; + } + for (l9 in c8) { + var c9 = c8[l9]; + if ((c9 && is_string(c9) && c9.indexOf(phrase) != -1) || l9.indexOf(phrase) != -1) { + console.log( + l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7 + " " + l8 + " " + l9, + ); + } + if (!is_object(c9)) { + continue; + } + for (l10 in c9) { + var c10 = c9[l10]; + if ((c10 && is_string(c10) && c10.indexOf(phrase) != -1) || l10.indexOf(phrase) != -1) { + console.log( + l1 + + " " + + l2 + + " " + + l3 + + " " + + l4 + + " " + + l5 + + " " + + l6 + + " " + + l7 + + " " + + l8 + + " " + + l9 + + " " + + l10, + ); + } + if (!is_object(c10)) { + continue; + } } } } @@ -3671,207 +5056,277 @@ function safe_search(obj,phrase) } } -function strip_string(str) -{ - var regexSymbolWithCombiningMarks = /([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g; - var regexLineBreakCombiningMarks = /[\0-\x08\x0E-\x1F\x7F-\x84\x86-\x9F\u0300-\u034E\u0350-\u035B\u0363-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u200C\u200E\u200F\u202A-\u202E\u2066-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3035\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFFF9-\uFFFB]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDE3E\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC35-\uDC46\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7]|\uD807[\uDC2F-\uDC36\uDC38-\uDC3F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E\uDCA0-\uDCA3]|\uD834[\uDD65-\uDD69\uDD6D-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD4A]|\uDB40[\uDC01\uDC20-\uDC7F\uDD00-\uDDEF]/g; +/* eslint-disable no-misleading-character-class,no-control-regex */ +function strip_string(str) { + var regexSymbolWithCombiningMarks = + /([\0-\u02FF\u0370-\u1AAF\u1B00-\u1DBF\u1E00-\u20CF\u2100-\uD7FF\uE000-\uFE1F\uFE30-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])([\u0300-\u036F\u1AB0-\u1AFF\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]+)/g; + var regexLineBreakCombiningMarks = + /[\0-\x08\x0E-\x1F\x7F-\x84\x86-\x9F\u0300-\u034E\u0350-\u035B\u0363-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u200C\u200E\u200F\u202A-\u202E\u2066-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3035\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFFF9-\uFFFB]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDCA-\uDDCC\uDE2C-\uDE37\uDE3E\uDEDF-\uDEEA\uDF00-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC35-\uDC46\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDEAB-\uDEB7]|\uD807[\uDC2F-\uDC36\uDC38-\uDC3F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E\uDCA0-\uDCA3]|\uD834[\uDD65-\uDD69\uDD6D-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD4A]|\uDB40[\uDC01\uDC20-\uDC7F\uDD00-\uDDEF]/g; // https://github.com/mathiasbynens/strip-combining-marks/blob/master/strip-combining-marks.js - return str.replace(regexLineBreakCombiningMarks, '').replace(regexSymbolWithCombiningMarks, '$1').replace(/[^\p{L}\p{N}\p{S}\p{P}\p{Z}]/gu, ''); // https://stackoverflow.com/a/63464318/914546 + return str + .replace(regexLineBreakCombiningMarks, "") + .replace(regexSymbolWithCombiningMarks, "$1") + .replace(/[^\p{L}\p{N}\p{S}\p{P}\p{Z}]/gu, ""); // https://stackoverflow.com/a/63464318/914546 } +/* eslint-enable no-misleading-character-class,no-control-regex */ // #HARDCORE -var hardcore_done=false; -function hardcore_loop() -{ - if(hardcore_done) return; - var m=0; - for(var id in players) - if(players[id].level>m) - E.rewards.leader=players[id].name,m=players[id].level; - if(E.minutes) - E.minutes-=1; - else - { - hardcore_done=true; +var hardcore_done = false; +function hardcore_loop() { + if (hardcore_done) { + return; + } + var m = 0; + for (var id in players) { + if (players[id].level > m) { + E.rewards.leader = players[id].name; + m = players[id].level; + } + } + if (E.minutes) { + E.minutes -= 1; + } else { + hardcore_done = true; send_hardcore_rewards(); shutdown_routine(); } - broadcast("hardcore_info",{E:E}); + broadcast("hardcore_info", { E: E }); } // #ACHIEVEMENTS -function send_hardcore_rewards() -{ - rd=[ - ["leader","Leadership",{"name":"xbox",q:1}], - ["item8","First +8 Item",{"name":"armorbox",q:10}], - ["item9","First +9 Item",{"name":"weaponbox",q:10}], - ["item10","First +X Item",{"name":"armorbox",q:100}], - ["item11","First +Y Item",{"name":"armorbox",q:100}], - ["item12","First +Z Item",{"name":"scroll3",q:1}], - ["accessory5","First +V Accessory",{"name":"armorbox",q:50}], - ["accessory6","First +S Accessory",{"name":"cscroll3",q:1}], - ["first_warrior_70","First Warrior to Level 70",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_paladin_70","First Paladin to Level 70",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_priest_70","First Priest to Level 70",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_mage_70","First Mage to Level 70",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_rogue_70","First Rogue to Level 70",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_ranger_70","First Ranger to Level 70",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_franky","First Franky Kill",{"name":"brownegg",q:1}], - ["first_stompy","First Stompy Kill",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_ent","First Ent Kill",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_wabbit","First Wabbit Kill",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_fvampire","First Ms.Vampire Kill",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_mvampire","First Mr.Vampire Kill",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_skeletor","First Skeletor Kill",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], - ["first_goo","First Goo Kill",{"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1}], +function send_hardcore_rewards() { + rd = [ + ["leader", "Leadership", { name: "xbox", q: 1 }], + ["item8", "First +8 Item", { name: "armorbox", q: 10 }], + ["item9", "First +9 Item", { name: "weaponbox", q: 10 }], + ["item10", "First +X Item", { name: "armorbox", q: 100 }], + ["item11", "First +Y Item", { name: "armorbox", q: 100 }], + ["item12", "First +Z Item", { name: "scroll3", q: 1 }], + ["accessory5", "First +V Accessory", { name: "armorbox", q: 50 }], + ["accessory6", "First +S Accessory", { name: "cscroll3", q: 1 }], + ["first_warrior_70", "First Warrior to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_paladin_70", "First Paladin to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_priest_70", "First Priest to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_mage_70", "First Mage to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_rogue_70", "First Rogue to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_ranger_70", "First Ranger to Level 70", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_franky", "First Franky Kill", { name: "brownegg", q: 1 }], + ["first_stompy", "First Stompy Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_ent", "First Ent Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_wabbit", "First Wabbit Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_fvampire", "First Ms.Vampire Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_mvampire", "First Mr.Vampire Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_skeletor", "First Skeletor Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], + ["first_goo", "First Goo Kill", { name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }], ]; - rd.forEach(function(r){ - if(E.rewards[r[0]]) - appengine_call("send_mail",{subject:"HARDCORE: Congratulations!",message:"Reward for "+r[1],type:"system",rid:randomStr(50),to:E.rewards[r[0]],item:JSON.stringify(r[2]),retries:8}); + rd.forEach(function (r) { + if (E.rewards[r[0]]) { + appengine_call("send_mail", { + subject: "HARDCORE: Congratulations!", + message: "Reward for " + r[1], + type: "system", + rid: randomStr(50), + to: E.rewards[r[0]], + item: JSON.stringify(r[2]), + retries: 8, + }); + } }); - for(var id in players) + for (var id in players) { save_player(players[id]); - var sent={}; - for(var id in P) - { - var p=P[id]; - if(p.auth_id && p.level>=60 && !sent[p.auth_id]) - { - sent[p.auth_id]=true; - appengine_call("send_mail",{subject:"HARDCORE: Congratulations!",message:"Reward for Participation",type:"system",rid:randomStr(50),to:p.name,item:JSON.stringify({"name":(Math.random()<0.1&&"gift0"||"gift1"),q:1})}); + } + var sent = {}; + for (var id in P) { + var p = P[id]; + if (p.auth_id && p.level >= 60 && !sent[p.auth_id]) { + sent[p.auth_id] = true; + appengine_call("send_mail", { + subject: "HARDCORE: Congratulations!", + message: "Reward for Participation", + type: "system", + rid: randomStr(50), + to: p.name, + item: JSON.stringify({ name: (Math.random() < 0.1 && "gift0") || "gift1", q: 1 }), + }); } } } -function add_achievement(player,name) -{ - player.p.achievements[name]=(player.p.achievements[name]||0)+1; - player.socket.emit("achievement_success",{name:name}); +function add_achievement(player, name) { + player.p.achievements[name] = (player.p.achievements[name] || 0) + 1; + player.socket.emit("achievement_success", { name: name }); } -function item_achievement_increment(player,item,ach,amount) -{ - if(!G.achievements[ach] || !item) return; - if(item.ach!=ach || !item.acc) - item.acc=0; - var needed=G.achievements[ach].count,old=item.acc,rr=G.achievements[ach].rr||1; - item.ach=ach; - item.acc+=(amount||1); - if(item.acc=70 && !E.rewards['first_'+c+'_70'] && E.minutes) E.rewards['first_'+c+'_70']=player.name,announce=true; - }); - if(announce) - broadcast("hardcore_info",{E:E,achiever:player.name}); - } - else - { - - } -}catch(e){console.log("#A: "+e);}} - -function achievement_logic_compound_success(player,item) -{try{ - if(gameplay=="hardcore") - { - var announce=false; - if(item.level==5 && !E.rewards.accessory5 && E.minutes) E.rewards.accessory5=player.name,announce=true; - if(item.level==6 && !E.rewards.accessory6 && E.minutes) E.rewards.accessory6=player.name,announce=true; - if(announce) - broadcast("hardcore_info",{E:E,achiever:player.name}); - } - else - { - - } -}catch(e){console.log("#A: "+e);}} - -function achievement_logic_upgrade_success(player,item) -{try{ - if(gameplay=="hardcore") - { - var announce=false; - if(item.level==8 && !E.rewards.item8 && E.minutes) E.rewards.item8=player.name,announce=true; - if(item.level==9 && !E.rewards.item9 && E.minutes) E.rewards.item9=player.name,announce=true; - if(item.level==10 && !E.rewards.item10 && E.minutes) E.rewards.item10=player.name,announce=true; - if(item.level==11 && !E.rewards.item11 && E.minutes) E.rewards.item11=player.name,announce=true; - if(item.level==12 && !E.rewards.item12 && E.minutes) E.rewards.item12=player.name,announce=true; - if(announce) - { - broadcast("hardcore_info",{E:E,achiever:player.name}); +function achievement_logic_monster_damage(player, monster, damage) { + try { + if (monster.type == "grinch") { + item_achievement_increment(player, player.slots.cape, "festive", damage); } + } catch (e) { + console.log("#A: " + e); } - else - { +} +function achievement_logic_monster_kill(player, monster) { + try { + if (gameplay == "hardcore") { + var announce = false; + ["ent", "stompy", "franky", "fvampire", "mvampire", "skeletor", "goo"].forEach(function (m) { + if (monster.type == m && !E.rewards["first_" + m] && E.minutes) { + E.rewards["first_" + m] = player.name; + announce = true; + } + }); + if (announce) { + broadcast("hardcore_info", { E: E, achiever: player.name }); + } + } else { + } + } catch (e) { + console.log("#A: " + e); } -}catch(e){console.log("#A: "+e);}} +} -function achievement_logic_monster_last_hit(player,monster) -{try{ - if(server.shutdown) return; - if(player.slots.mainhand) delete player.slots.mainhand.ach; -}catch(e){console.log("#A: "+e);}} +function achievement_logic_level(player) { + try { + if (gameplay == "hardcore") { + var announce = false; + ["warrior", "paladin", "priest", "mage", "ranger", "rogue", "wabbit"].forEach(function (c) { + if (player.type == c && player.level >= 70 && !E.rewards["first_" + c + "_70"] && E.minutes) { + E.rewards["first_" + c + "_70"] = player.name; + announce = true; + } + }); + if (announce) { + broadcast("hardcore_info", { E: E, achiever: player.name }); + } + } else { + } + } catch (e) { + console.log("#A: " + e); + } +} -function achievement_logic_monster_hit(monster,player,attack) -{try{ - if(monster.type=="stompy" && !monster.target) - item_achievement_increment(player,player.slots.helmet,"stomped"); - else if(monster.type!="stompy" && player.slots.helmet) - delete player.slots.helmet.ach; +function achievement_logic_compound_success(player, item) { + try { + if (gameplay == "hardcore") { + var announce = false; + if (item.level == 5 && !E.rewards.accessory5 && E.minutes) { + E.rewards.accessory5 = player.name; + announce = true; + } + if (item.level == 6 && !E.rewards.accessory6 && E.minutes) { + E.rewards.accessory6 = player.name; + announce = true; + } + if (announce) { + broadcast("hardcore_info", { E: E, achiever: player.name }); + } + } else { + } + } catch (e) { + console.log("#A: " + e); + } +} - if(monster.type=="goo" || monster.type=="cgoo") - item_achievement_increment(player,player.slots.pants,"gooped",attack); +function achievement_logic_upgrade_success(player, item) { + try { + if (gameplay == "hardcore") { + var announce = false; + if (item.level == 8 && !E.rewards.item8 && E.minutes) { + E.rewards.item8 = player.name; + announce = true; + } + if (item.level == 9 && !E.rewards.item9 && E.minutes) { + E.rewards.item9 = player.name; + announce = true; + } + if (item.level == 10 && !E.rewards.item10 && E.minutes) { + E.rewards.item10 = player.name; + announce = true; + } + if (item.level == 11 && !E.rewards.item11 && E.minutes) { + E.rewards.item11 = player.name; + announce = true; + } + if (item.level == 12 && !E.rewards.item12 && E.minutes) { + E.rewards.item12 = player.name; + announce = true; + } + if (announce) { + broadcast("hardcore_info", { E: E, achiever: player.name }); + } + } else { + } + } catch (e) { + console.log("#A: " + e); + } +} -}catch(e){console.log("#A: "+e);}} +function achievement_logic_monster_last_hit(player, monster) { + try { + if (server.shutdown) { + return; + } + if (player.slots.mainhand) { + delete player.slots.mainhand.ach; + } + } catch (e) { + console.log("#A: " + e); + } +} -function achievement_logic_burn_last_hit(player) -{try{ - if(server.shutdown) return; - item_achievement_increment(player,player.slots.mainhand,"firehazard"); -}catch(e){console.log("#A: "+e);}} \ No newline at end of file +function achievement_logic_monster_hit(monster, player, attack) { + try { + if (monster.type == "stompy" && !monster.target) { + item_achievement_increment(player, player.slots.helmet, "stomped"); + } else if (monster.type != "stompy" && player.slots.helmet) { + delete player.slots.helmet.ach; + } + + if (monster.type == "goo" || monster.type == "cgoo") { + item_achievement_increment(player, player.slots.pants, "gooped", attack); + } + } catch (e) { + console.log("#A: " + e); + } +} + +function achievement_logic_burn_last_hit(player) { + try { + if (server.shutdown) { + return; + } + item_achievement_increment(player, player.slots.mainhand, "firehazard"); + } catch (e) { + console.log("#A: " + e); + } +} diff --git a/node/server_worker.js b/node/server_worker.js index 0e98fff..69d837f 100644 --- a/node/server_worker.js +++ b/node/server_worker.js @@ -1,23 +1,28 @@ -var is_game=0,is_server=1,is_code=0; -var variables=require('./variables'); -var is_sdk=variables.is_sdk; -var fs=require('fs'); -eval(""+fs.readFileSync(variables.cfunctions_path)); -eval(""+fs.readFileSync(variables.functions_path)); -var {workerData,parentPort}=require('worker_threads'); -var G=workerData.G,smap_data=workerData.smap_data,amap_data=workerData.amap_data; +var is_game = 0; +var is_server = 1; +var is_code = 0; +var variables = require("./variables"); +var is_sdk = variables.is_sdk; +var fs = require("fs"); +eval("" + fs.readFileSync(variables.cfunctions_path)); +eval("" + fs.readFileSync(variables.functions_path)); +var { workerData, parentPort } = require("worker_threads"); +var G = workerData.G; +var smap_data = workerData.smap_data; +var amap_data = workerData.amap_data; -parentPort.on("message",function(data){ - try{ +parentPort.on("message", function (data) { + try { //console.log(data); - if(data.type=="fast_astar") - parentPort.postMessage({type:"monster_move",move:fast_astar(data),id:data.id,in:data.in}); - if(data.type=="exit") process.exit(); - }catch(e){ + if (data.type == "fast_astar") { + parentPort.postMessage({ type: "monster_move", move: fast_astar(data), id: data.id, in: data.in }); + } + if (data.type == "exit") { + process.exit(); + } + } catch (e) { console.log(e); } }); -setInterval(function(){ - -},10); \ No newline at end of file +setInterval(function () {}, 10); diff --git a/node/test.js b/node/test.js index d2f5910..363e692 100644 --- a/node/test.js +++ b/node/test.js @@ -1,13 +1,14 @@ -var crypto=require('crypto'); -var protobuf=require('protobufjs'),ByteBuffer=require('bytebuffer'); +var crypto = require("crypto"); +var protobuf = require("protobufjs"); +var ByteBuffer = require("bytebuffer"); function symmetricDecrypt(input, key, checkHmac) { - var aesIv = crypto.createDecipheriv('aes-256-ecb', key, ''); + var aesIv = crypto.createDecipheriv("aes-256-ecb", key, ""); aesIv.setAutoPadding(false); aesIv.end(input.slice(0, 16)); var iv = aesIv.read(); - var aesData = crypto.createDecipheriv('aes-256-cbc', key, iv); + var aesData = crypto.createDecipheriv("aes-256-cbc", key, iv); aesData.end(input.slice(16)); var plaintext = aesData.read(); @@ -24,7 +25,7 @@ function symmetricDecrypt(input, key, checkHmac) { } return plaintext; -}; +} function parseAppTicket(ticket) { // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl @@ -70,11 +71,16 @@ function parseAppTicket(ticket) { // Start reading the ownership ticket let ownershipTicketOffset = ticket.offset; let ownershipTicketLength = ticket.readUint32(); // including itself, for some reason - if (ownershipTicketOffset + ownershipTicketLength != ticket.limit && ownershipTicketOffset + ownershipTicketLength + 128 != ticket.limit) { + if ( + ownershipTicketOffset + ownershipTicketLength != ticket.limit && + ownershipTicketOffset + ownershipTicketLength + 128 != ticket.limit + ) { return null; } - let i, j, dlc; + let i; + let j; + let dlc; details.version = ticket.readUint32(); details.steamID = ticket.readUint64().toString(); @@ -117,34 +123,54 @@ function parseAppTicket(ticket) { let date = new Date(); details.isExpired = details.ownershipTicketExpires < date; - details.hasValidSignature = !!details.signature && SteamCrypto.verifySignature(ticket.slice(ownershipTicketOffset, ownershipTicketOffset + ownershipTicketLength).toBuffer(), details.signature); + details.hasValidSignature = + !!details.signature && + SteamCrypto.verifySignature( + ticket.slice(ownershipTicketOffset, ownershipTicketOffset + ownershipTicketLength).toBuffer(), + details.signature, + ); details.isValid = !details.isExpired && (!details.signature || details.hasValidSignature); } catch (ex) { - console.log("parseAppTicket: "+ex); + console.log("parseAppTicket: " + ex); return details; return null; // not a valid ticket } return details; +} + +var proto = { + nested: { + EncryptedAppTicket: { + fields: { + ticketVersionNo: { type: "uint32", id: 1 }, + crcEncryptedticket: { type: "uint32", id: 2 }, + cbEncrypteduserdata: { type: "uint32", id: 3 }, + cbEncryptedAppownershipticket: { type: "uint32", id: 4 }, + encryptedTicket: { type: "bytes", id: 5 }, + }, + }, + }, }; +var proto_root = protobuf.Root.fromJSON(proto); +var EncryptedAppTicket = proto_root.lookupType("EncryptedAppTicket"); -var proto={ "nested": {EncryptedAppTicket: { fields: - { ticketVersionNo: { type: 'uint32', id: 1 }, - crcEncryptedticket: { type: 'uint32', id: 2 }, - cbEncrypteduserdata: { type: 'uint32', id: 3 }, - cbEncryptedAppownershipticket: { type: 'uint32', id: 4 }, - encryptedTicket: { type: 'bytes', id: 5 } } }}}; -var proto_root=protobuf.Root.fromJSON(proto); -var EncryptedAppTicket=proto_root.lookupType("EncryptedAppTicket"); - - -var outer=EncryptedAppTicket.decode(new Buffer("080110a7e59890031815204f2a80014527ad8cccd8cd9b7d3260529556004ddf0454a1c2e8543495b1eb72b3dff44d9d67555798ef44b7e3f97cf86d3bf1bc3f84a91de415c382b0d96033c8e5398f2b775bcb004f5487c651d81ef410d7d2917cbb407713397d2c29a455b6852eac7db1a0e52248585bd5440ef07de54b7519f67eefbad5420d97b77ef7e35450af","hex")); -var decrypted=symmetricDecrypt(outer.encryptedTicket,new Buffer("8af5c487af6c7709ef1a4e6e2275fcc0a90f57ae4319de793fca3d7ed8655520","hex")); +var outer = EncryptedAppTicket.decode( + new Buffer( + "080110a7e59890031815204f2a80014527ad8cccd8cd9b7d3260529556004ddf0454a1c2e8543495b1eb72b3dff44d9d67555798ef44b7e3f97cf86d3bf1bc3f84a91de415c382b0d96033c8e5398f2b775bcb004f5487c651d81ef410d7d2917cbb407713397d2c29a455b6852eac7db1a0e52248585bd5440ef07de54b7519f67eefbad5420d97b77ef7e35450af", + "hex", + ), +); +var decrypted = symmetricDecrypt( + outer.encryptedTicket, + new Buffer("8af5c487af6c7709ef1a4e6e2275fcc0a90f57ae4319de793fca3d7ed8655520", "hex"), +); let userData = decrypted.slice(0, outer.cbEncrypteduserdata); let ownershipTicketLength = decrypted.readUInt32LE(outer.cbEncrypteduserdata); -let ownershipTicket = parseAppTicket(decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength)); +let ownershipTicket = parseAppTicket( + decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), +); if (ownershipTicket) { ownershipTicket.userData = userData.toString(); } console.log(ownershipTicket); - diff --git a/useful/template.live_variables.js b/useful/template.live_variables.js index cc4e57d..c622edd 100644 --- a/useful/template.live_variables.js +++ b/useful/template.live_variables.js @@ -1,18 +1,18 @@ -module.exports={ - "cfunctions_path":"./server/common_functions.js", - "functions_path":"./server/server_functions.js", - "worker_path":"./server/server_worker.js", - "data_path":"./server/data.js", - "base_url":"https://yourappspoturldontincludedashes.appspot.com", - "keyword":"123", - "access_master":"123", - "bot_key":"123", - "discord_token":"NDXXXXXXXXXXX...", - "apple_token":"acXXXXXXXX...", - "steam_key":"8aXXXXXXXXX...", - "steam_web_key":"B4XXXXXXX...", - "steam_partner_key":"F9XXXXXXX...", - "close_timeout":24000, - "ip_limit":3, - "character_limit":3, -} +module.exports = { + cfunctions_path: "./server/common_functions.js", + functions_path: "./server/server_functions.js", + worker_path: "./server/server_worker.js", + data_path: "./server/data.js", + base_url: "https://yourappspoturldontincludedashes.appspot.com", + keyword: "123", + access_master: "123", + bot_key: "123", + discord_token: "NDXXXXXXXXXXX...", + apple_token: "acXXXXXXXX...", + steam_key: "8aXXXXXXXXX...", + steam_web_key: "B4XXXXXXX...", + steam_partner_key: "F9XXXXXXX...", + close_timeout: 24000, + ip_limit: 3, + character_limit: 3, +}; diff --git a/useful/template.variables.js b/useful/template.variables.js index bf90732..c44d461 100644 --- a/useful/template.variables.js +++ b/useful/template.variables.js @@ -1,22 +1,22 @@ -const path = require("node:path") +const path = require("node:path"); -module.exports={ - "cfunctions_path":path.resolve(__dirname, "../js/common_functions.js"), - "functions_path":path.resolve(__dirname, "server_functions.js"), - "worker_path":path.resolve(__dirname, "server_worker.js"), - "data_path":path.resolve(__dirname, "data.js"), - "base_url":"http://thegame.com", - "keyword":"123", - "access_master":"123", - "bot_key":"123", - "discord_token":"NDXXXXXXXXXXX...", - "apple_token":"acXXXXXXXX...", - "steam_key":"8aXXXXXXXXX...", - "steam_web_key":"B4XXXXXXX...", - "steam_partner_key":"F9XXXXXXX...", - "is_sdk":1, - "close_timeout":4000, - "ip_limit":3, - "character_limit":3, - "fast_sdk":0, -} +module.exports = { + cfunctions_path: path.resolve(__dirname, "../js/common_functions.js"), + functions_path: path.resolve(__dirname, "server_functions.js"), + worker_path: path.resolve(__dirname, "server_worker.js"), + data_path: path.resolve(__dirname, "data.js"), + base_url: "http://thegame.com", + keyword: "123", + access_master: "123", + bot_key: "123", + discord_token: "NDXXXXXXXXXXX...", + apple_token: "acXXXXXXXX...", + steam_key: "8aXXXXXXXXX...", + steam_web_key: "B4XXXXXXX...", + steam_partner_key: "F9XXXXXXX...", + is_sdk: 1, + close_timeout: 4000, + ip_limit: 3, + character_limit: 3, + fast_sdk: 0, +};