From f62ec0ab4ef37de1f583f328508facf622896515 Mon Sep 17 00:00:00 2001 From: Telokis Date: Sat, 28 Oct 2023 15:21:11 +0200 Subject: [PATCH 1/8] Add eslint and prettier configuration and dependencies --- node/.eslintrc | 25 + node/.prettierrc | 9 + node/package-lock.json | 2914 +++++++++++++++++++++++++++++++++++++++- node/package.json | 26 +- 4 files changed, 2941 insertions(+), 33 deletions(-) create mode 100644 node/.eslintrc create mode 100644 node/.prettierrc diff --git a/node/.eslintrc b/node/.eslintrc new file mode 100644 index 00000000..9ae15de3 --- /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", + "no-empty": "warn", + "no-inner-declarations": "warn", + "no-unreachable": "warn", + "no-undef": "warn", + "no-unused-vars": "warn", + "no-redeclare": "warn", + "curly": "error", + "indent": ["error", 2], + "prettier/prettier": "error", + "one-var": ["error", "never"], + "no-sequences": "error" + } +} diff --git a/node/.prettierrc b/node/.prettierrc new file mode 100644 index 00000000..e39f1de3 --- /dev/null +++ b/node/.prettierrc @@ -0,0 +1,9 @@ +{ + "tabWidth": 2, + "singleQuote": false, + "printWidth": 120, + "trailingComma": "all", + "semi": true, + "bracketSpacing": true, + "arrowParens": "always" +} \ No newline at end of file diff --git a/node/package-lock.json b/node/package-lock.json index fa7d787c..5e7075f5 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", @@ -459,51 +1544,290 @@ "node": ">= 0.6" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "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", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "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", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "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": { - "mime-db": "1.52.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "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": ">= 0.6" + "node": ">=6" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "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": "*" + "node": ">=8" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "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 3267a6b0..42a750f4 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 From 988bb21c3a2f20b4b6fc951258306c0228ac3fe7 Mon Sep 17 00:00:00 2001 From: Telokis Date: Sat, 28 Oct 2023 15:29:14 +0200 Subject: [PATCH 2/8] Add VSCode specific files --- .vscode/extensions.json | 3 +++ .vscode/settings.json | 12 ++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..1d7ac851 --- /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 00000000..13ad47b9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "prettier.configPath": "./node/.prettierrc", + "prettier.printWidth": 120, + "prettier.tabWidth": 2, + "prettier.trailingComma": "all", + "prettier.semi": true, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } +} From 5fea13946a7f7336a01d7231c4644c3e3ee44873 Mon Sep 17 00:00:00 2001 From: Telokis Date: Sat, 28 Oct 2023 15:21:23 +0200 Subject: [PATCH 3/8] Add github action ensuring linting --- .github/workflows/code_quality.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/code_quality.yml diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml new file mode 100644 index 00000000..b05889fc --- /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 From 8099f31e4ca0331471787437edb1910c61d9138b Mon Sep 17 00:00:00 2001 From: Telokis Date: Sat, 28 Oct 2023 15:21:37 +0200 Subject: [PATCH 4/8] Autofix all files for the game server --- node/.eslintrc | 2 +- node/precompute.js | 60 +- node/server.js | 22840 +++++++++++++++++----------- node/server_functions.js | 9101 ++++++----- node/server_worker.js | 45 +- node/test.js | 306 +- useful/template.live_variables.js | 36 +- useful/template.variables.js | 42 +- 8 files changed, 19132 insertions(+), 13300 deletions(-) diff --git a/node/.eslintrc b/node/.eslintrc index 9ae15de3..a806da0d 100644 --- a/node/.eslintrc +++ b/node/.eslintrc @@ -10,6 +10,7 @@ "rules": { "no-constant-condition": "off", "no-dupe-else-if": "off", + "indent": "off", "no-empty": "warn", "no-inner-declarations": "warn", "no-unreachable": "warn", @@ -17,7 +18,6 @@ "no-unused-vars": "warn", "no-redeclare": "warn", "curly": "error", - "indent": ["error", 2], "prettier/prettier": "error", "one-var": ["error", "never"], "no-sequences": "error" diff --git a/node/precompute.js b/node/precompute.js index d5d258af..13b73ec5 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"); - } - 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 +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"); + } + 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 c594c53c..28bf769f 100644 --- a/node/server.js +++ b/node/server.js @@ -1,9444 +1,13782 @@ -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, - // 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, +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, }; -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] - //#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 +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 }; -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 - // RANDOM - "halloween":true, - "goblin":false, - "goldenbat":160000, - "cutebee":960000, - "hide_and_seek":0, - // DAILIES - "goobrawl":false, - "crabxx":false, - "abtesting":false, - // NIGHTLIES - "icegolem":false, - "franky":false, +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 + // RANDOM + halloween: true, + goblin: false, + goldenbat: 160000, + cutebee: 960000, + hide_and_seek: 0, + // DAILIES + goobrawl: false, + crabxx: false, + abtesting: false, + // NIGHTLIES + 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; - - -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]; - -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; -} -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; +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]; + +E.schedule.time_offset = TIMEO[region]; + +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; +} 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=="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; - // variables.ip_limit=2+1; - // variables.character_limit=1+1; - luckm*=1.15; - xpm*=1.20; - goldm*=1.25; +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.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) - { - log_trace("init",e); - } - },function(){ - console.log("#X init_game's create_server call failed"); - }); +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) { + 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 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","+"); - return result; +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 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); - } - return json; +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", "+"); + return result; } -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); - } - } - 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); - } - response.writeHead(200); - response.end(output); - }catch(e){ - log_trace("chttp_err",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 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 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); + 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); + } + response.writeHead(200); + response.end(output); + } catch (e) { + log_trace("chttp_err", e); + } + }); } -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]; - }); - 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) - { - // data.id="$"+data.id; - 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]; - }); - 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); - } - return data; +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 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]; - }); - 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(events && events.length) data.events=events; - return data; +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]; + } + }); + 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) { + // data.id="$"+data.id; + 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]; + }); + 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); + } + } + 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||{}); - return summary; +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.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; + } + return data; } -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 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 clean_slate(player) -{ - restore_state(player); - save_state(player); - player.hp=player.max_hp; - player.mp=player.max_mp; - player.s={}; - player.c={}; +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 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; - } - player.state.restored=true; - } - delete player.team; - delete player.duel; +function clean_slate(player) { + restore_state(player); + save_state(player); + player.hp = player.max_hp; + player.mp = player.max_mp; + player.s = {}; + player.c = {}; } -function party_to_client(oname) -{ - var list=parties[oname]; - var party={},output=0,length=0,newbies=0,odps={},c={},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]],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); - } - 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); - }); - // git test - return party; +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; + } + player.state.restored = true; + } + delete player.team; + delete player.duel; } -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 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]]; + 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]]; + var 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); + } 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; + } + }); + // git test + return 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 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 }); + }); } -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 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 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; - player.level++; - 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}); - } - // 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) - { - console.log("'Your monster!' logged out ->"); - 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; - }); - 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={}; - // 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); - player.stones++; - 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(mx80)&&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; - - 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); - recalculate_vxy(player); - perfc.cps+=1; +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; + 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 }); + } + // 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) { + console.log("'Your monster!' logged out ->"); + player.socket.emit("ui_log", "You monster!"); + player.socket.disconnect(); + return; + } + } + 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; + }); + [ + "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 = {}; + } + // 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; 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.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 < def[0]) { + return; + } + if (def[1] == "stat") { + player.monster_stats[def[2]] = (player.monster_stats[def[2]] || 0) + def[3]; + } + }); + } + } + character_slots.forEach(function (slot) { + var current = player.slots[slot]; + if (!current) { + return; + } + var def = G.items[current.name]; + if (!def) { + console.log("#X Undefined item: " + current.name + " (" + slot + ")"); + return; + } + var prop = calculate_item_properties(current, { class: player.type, map: player.map }); + if (prop.class && !prop.class.includes(player.type)) { + return; + } + + apply_stats(player, prop, { no_range: slot == "offhand" && def.type == "weapon" }); + if (prop.attack) { + if (slot == "offhand") { + item_attack += prop.attack * 0.7; + } else { + item_attack += prop.attack; + } + } + if (def.ability) { + player.a[def.ability] = { + attr0: ((player.a[def.ability] && player.a[def.ability].attr0) || 0) + prop.attr0, + attr1: ((player.a[def.ability] && player.a[def.ability].attr1) || 0) + prop.attr1, + }; + } + if (def.aura) { + player.aura[def.aura] = { attr0: prop.attr0 || 0, attr1: prop.attr1 || 0 }; + } + if (slot == "mainhand") { + apply_stats(player, class_def.doublehand[def.wtype] || class_def.mainhand[def.wtype] || {}); + } + if (slot == "offhand") { + apply_stats( + player, + class_def.offhand[def.wtype] || + class_def.offhand[def.type] || { + no_range: !player.slots.mainhand, + }, + ); + } + if (prop.set) { + player.sets[def.set] = (player.sets[def.set] || 0) + 1; + } + }); + for (var set in player.sets) { + var prop = G.sets[set] && G.sets[set][player.sets[set]]; + if (!prop) { + continue; + } + apply_stats(player, prop); + } + for (var condition in player.s) { + var prop = G.conditions[condition]; + apply_stats(player, player.s[condition]); + if (!prop) { + continue; + } + apply_stats(player, prop); + } + apply_stats(player, player.monster_stats); + if ( + player.slots.mainhand && + player.slots.offhand && + G.items[player.slots.mainhand.name].wtype == "stars" && + G.items[player.slots.offhand.name].wtype != "stars" + ) { + item_attack /= 3.0; + } + if (player.slots.cape && player.slots.cape.name == "stealthcape") { + player.stealth = true; + } + item_attack = max(item_attack, 5); + if (player.type == "paladin") { + player.attack += item_attack * (player.str / 20.0 + player.int / 40.0); + } else { + player.attack += item_attack * (player[class_def.main_stat] / 20.0); + } + player.attack += player.a_attack; + if (player.type == "priest") { + player.attack *= 1.6; + } + if (player.type == "warrior") { + player.courage += round(player.str / 30); + } + if (player.type == "priest") { + player.mcourage += round(player.int / 30); + } + if (player.type == "paladin") { + player.pcourage += round(player.str / 30 + player.int / 30); + } + //console.log(player.speed) + //player.speed+=player.dex/20.0+player.str/40.0+min(player.level,40)/4.0+max(0,min(player.level-40,20))/5.0+max(0,player.level-60)/7.0 + player.speed += + min(player.dex, 256) / 32.0 + + min(player.str, 256) / 64.0 + + min(player.level, 40) / 10.0 + + max(0, min(player.level - 40, 20)) / 15.0 + + max(0, min(86, player.level) - 60) / 16.0; + + player.aggro_diff = player.bling / 100 - player.cuteness / 100; + + //console.log(player.speed) + //player.max_hp+=player.str*5+player.vit*player.level/2; //player.str*10.0+player.level*10+player.vit*25 + player.max_hp += player.str * 21 + player.vit * (48 + player.level / 3.0); + player.max_hp = max(1, player.max_hp); + player.max_mp += player.int * 15.0 + player.level * 5; + //player.armor+=player.str/2.0; + player.armor += min(player.str, 160) + max(0, player.str - 160) * 0.25; + player.resistance += min(player.int, 180) + max(0, player.int - 180) * 0.25; + player.frequency += + min(player.level, 80) / 164.0 + + min(160, player.dex) / 640.0 + + max(player.dex - 160, 0) / 925.0 + + player.int / 1575.0; // 120 635 1275 is the original mix + // player.frequency=9000; + player.attack_ms = round(1000.0 / player.frequency); + if (player.last_attack_ms && player.attack_ms != player.last_attack_ms) { + // server_log("skill_timeout_correction: "+player.last_attack_ms+" to "+player.attack_ms+" timeout: "+(player.attack_ms-mssince(player.last.attack))) + player.socket.emit("skill_timeout", { + name: "attack", + ms: player.attack_ms - mssince(player.last.attack), + reason: "attack_ms", + }); + } + player.last_attack_ms = player.attack_ms; + player.mp_cost += + min(player.level, 80) * (player.mp_cost / 10.0) + + player.a_mp_cost + + player.crit * 1.25 + + player.lifesteal * 1.5 + + player.manasteal / 5.0; + if (player.damage_type == "physical") { + player.mp_cost += player.apiercing / 15.0; + } else { + player.mp_cost += player.rpiercing / 15.0; + } + player.mp_cost = max(1, player.mp_cost); + if (!player.hp && !player.rip) { + player.hp = player.max_hp; + player.mp = player.max_mp; + } // used for level-ups + if ((player.gold || 0) <= 0) { + player.gold = 0; + } + player.heal = 0; + if (player.type == "priest") { + player.heal = player.attack; + } + player.output = max(5, player.output); + if (player.output) { + player.attack = (player.attack * player.output) / 100.0; + } + if (player.s.damage_received) { + player.attack += (player.s.damage_received.amount * 4) / 100; + } + ["attack", "heal", "hp", "mp", "max_hp", "max_mp", "range", "mp_cost", "resistance", "armor"].forEach( + function (prop) { + player[prop] = round(player[prop]); + }, + ); + player.hp = max(0, min(player.hp, player.max_hp)); + player.mp = max(0, min(player.mp, player.max_mp)); + + if (player.party && parties[player.party]) { + if (player.party_xp) { + player.xxp += player.party_xp; + } + if (player.party_luck) { + player.xluck += player.party_luck; + } + if (player.party_gold) { + player.xgold += player.party_gold; + } + } + + if (goldm != 1) { + player.xgold += (goldm - 1) * 100.0; + player.xluck += (luckm - 1) * 100.0; + player.xxp += (xpm - 1) * 100.0; + } + player.luckm = 1 + player.xluck / 100.0; + player.xpm = 1 + player.xxp / 100.0; + player.goldm = 1 + player.xgold / 100.0; + ["luckm", "xpm", "goldm"].forEach(function (p) { + player[p] = max(0.01, player[p]); + }); + + if (player.tskin == "konami") { + player.range = 120; + player.frequency += 0.25; + player.goldm *= 0.25; + player.luckm += 0.25; + } + + 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); + + player.tax = + (player.level > 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; + + 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); + } + recalculate_vxy(player); + 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; - }); - 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]) - // if(p in 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); - } - 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; - } - if(E.schedule.night) monster.speed=ceil(monster.speed*0.7); - calculate_common_stats(monster); - recalculate_vxy(monster); +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) { + // if(G.conditions[name]) + // { + // for(var p in G.conditions[name]) + // if(p in 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); + } + 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); + } + 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"; - } - 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(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) - { - return "no"; - } - return slot; +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 (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) { + 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() < D.drops.gold.x10) { + drop.gold *= 10; + chest = "chest4"; + } // previously 12 + if (Math.random() < D.drops.gold.x50) { + drop.gold *= 50; + chest = "chest5"; + } // previously 200 + if (drop.items.length || drop.cash) { + chest = "chest6"; + } + drop.date = new Date(); + if (player.party) { + var owners = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && !owners.includes(current.owner)) { + owners.push(current.owner); + } + }); + party_emit( + player.party, + "drop", + { + x: drop.x, + y: drop.y, + items: drop.items.length, + chest: chest, + id: drop_id, + party: player.party, + map: drop.map, + owners: owners, + }, + { instance: player.in }, + ); + } else { + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.items.length, + chest: chest, + id: drop_id, + map: drop.map, + owners: [player.owner], + }); + } } -function drop_something_hardcore(player,target) -{ - var drop_id=randomStr(30),drop; - drop=chests[drop_id]={items:[],pvp_items:[],cash:0}; - drop.gold=99; - drop.x=target.x; - drop.y=target.y+10; - drop.map=target.map; - - target.slots.elixir=target.cslots.elixir=null; - - for(var name in target.slots) - if(target.slots[name] && !target.slots[name].b) - { - var prob=0.1; - if(name=="mainhand" || name=="offhand") prob=0.05; - 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 - } - 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; - return score; +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 + } + 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; + } + 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]); - } - 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"); + } + } } -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; - } - - - 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); - } - - 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(!is_same(attacker,target,1)) attacker.kills++; - - 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]; - - - 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;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; + } + + 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); + } + + 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 (!is_same(attacker, target, 1)) { + attacker.kills++; + } + + 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]; + + target.socket.emit("game_log", { message: "Slain by " + attacker.name, color: "#F12F02" }); + var lost_shells = 0; + var psize = 1; + if (lost_xp) { + for (var i = 0; i < target.isize; i++) { + if (target.items[i] && target.items[i].name == "xptome") { + lost_shells = 2; + lost_xp = floor(lost_xp / 50); + consume_one(target, i); + target.to_reopen = true; + target.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); + break; + } + } + } + + target.gold -= lost_gold; + target.xp -= lost_xp; + if (target.xp < 0) { + target.xp = 0; + } + target.socket.emit("game_log", "Lost " + to_pretty_num(lost_gold) + " gold"); + target.socket.emit("game_log", "Lost " + to_pretty_num(lost_xp) + " experience"); + target.socket.emit("disappearing_text", { + message: "-" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); + + if (gameplay == "hardcore") { + drop_something_hardcore(attacker, target); + } + if (is_in_pvp(target)) { + drop_something_pvp(attacker, target); + } + + if (!attacker.party) { + if (attacker.type == "merchant") { + lost_xp = 0; + } + attacker.gold += gain_gold; + attacker.xp += round(lost_xp * 0.95); + attacker.socket.emit("game_log", { message: "Pwned " + target.name, color: "#67C051" }); + attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); + attacker.socket.emit("disappearing_text", { + message: "+" + gain_gold, + x: target.x, + y: target.y - 40, + args: { color: "+gold", size: "large" }, + }); + attacker.socket.emit("game_log", "Gained " + to_pretty_num(round(lost_xp * 0.95)) + " experience"); + attacker.socket.emit("disappearing_text", { + message: "+" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); + if (lost_shells) { + add_shells(attacker, lost_shells, "xptome"); + } + } else { + var name = attacker.name; + lost_xp = floor((lost_xp * 0.92) / parties[attacker.party].length); + gain_gold = floor(gain_gold / parties[attacker.party].length); + if (lost_shells) { + add_shells(attacker, lost_shells, "xptome"); + } + // ,lost_shells=ceil(lost_shells/parties[attacker.party].length) + parties[attacker.party].forEach(function (a_name) { + var attacker = players[name_to_id[a_name]]; + attacker.gold += gain_gold; + if (attacker.type != "merchant") { + attacker.xp += lost_xp; + } + attacker.socket.emit("game_log", { message: name + " pwned " + target.name, color: "#67C051" }); + attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); + attacker.socket.emit("disappearing_text", { + message: "+" + gain_gold, + x: target.x, + y: target.y - 40, + args: { color: "+gold", size: "large" }, + }); + if (attacker.type != "merchant") { + attacker.socket.emit("game_log", "Gained " + to_pretty_num(lost_xp) + " experience"); + attacker.socket.emit("disappearing_text", { + message: "+" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); + } + }); + } } -function commence_attack(attacker,target,atype) -{ - var attack=attacker.attack,mp_cost=0; - var info={apiercing:0,damage_type:attacker.damage_type,heal:false,lines:true,positive:false,first_attack:0,procs:false,conditions:[]}; // server projectile - - if(!G.skills[atype].hostile) info.positive=true; - - // TARGET CHANGE - if(attacker.is_player) attacker.target=target.id; - - // FAILURE SCENARIOS - if(attacker.type=="merchant" && !G.skills[atype].merchant_use && !(atype=="attack" && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].wtype=="dartgun")) - { - attacker.socket.emit("game_response",{response:"attack_failed",id:target.id}); - return {failed:true,reason:"merchant",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}; - - // 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; - - // 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; - - // PROCS - 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} - } - - // 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; - - // 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(info.procs && attacker.s.poisonous) info.conditions.push("poisoned"); - - 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") - { - 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(!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); - - 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; - 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(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() 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); + 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; + } + + // 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; + } + + // PROCS + 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 }; + } + + // 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; + } + + // 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 (info.procs && attacker.s.poisonous) { + info.conditions.push("poisoned"); + } + + 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 ( + (!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); + + 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; + } + 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 (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() < (attacker.a.freeze.attr0 * (G.maps[attacker.map].freeze_multiplier || 1)) / 100.0 + ) { + info.conditions.push("frozen"); + } + if ( + info.procs && + attacker.a.burn && + info.procs && + Math.random() < (attacker.a.burn.attr0 * (G.maps[attacker.map].burn_multiplier || 1)) / 100.0 + ) { + info.conditions.push("burned"); + } + if (info.procs && attacker.a.weave && info.procs) { + info.conditions.push("woven"); + } + if (info.procs && attacker.stun && Math.random() < attacker.stun / 100.0 && info.damage_type == "physical") { + info.conditions.push("stunned"); + } + + var pid = randomStr(6); + var eta = 0; + info.first_attack = info.attack = attack; + info.def = def; + def.damage_type = info.damage_type; + def.pid = pid; + info.attacker = attacker; + info.target = target; + info.atype = atype; + projectiles[pid] = info; + + var action = { + attacker: attacker.id, + target: target.id, + type: atype, + source: atype, + x: target.x, + y: target.y, + eta: 400, + m: target.m, + pid: pid, + }; + + if (def.projectile) { + action.projectile = def.projectile; + if (target.is_monster && G.monsters[target.type].escapist) { + var dampened = false; + for (var id in instances[target.in].monsters) { + var m = instances[target.in].monsters[id]; + if (m.type == "fieldgen0" && point_distance(target.x, target.y, m.x, m.y) < 300) { + target.s.dampened = { ms: 2000 }; + add_condition(target, "dampened", { ms: 2000 }); + dampened = true; + } + } + if (!dampened) { + port_monster(target, random_place(target.map)); + } + } + } + + if (!(G.projectiles[def.projectile] && G.projectiles[def.projectile].instant)) { + eta = (1000 * dist) / G.projectiles[def.projectile].speed; + } else { + action.instant = true; + } + + info.eta = future_ms(eta); + + if (info.heal) { + action.heal = attack; + } else if (attack) { + action.damage = attack; + } + if (info.positive) { + action.positive = true; + } + if (info.conditions.length) { + action.conditions = info.conditions; + } + info.action = action; + xy_emit(attacker, "action", action, target.id); + + if (!eta) { + projectiles_loop(); + } + + action.response = "data"; + action.place = atype; + + return action; } -function complete_attack(attacker,target,info) -{ - var defense="armor",pierce="apiercing"; - var combo=1,combo_m=1; - var atype=info.atype; - var evade=false,first=true; - var attack=info.attack,o_attack,i_attack=info.attack,def=info.def; - if(G.monsters[attacker.type] && G.monsters[attacker.type].good) info.heal=info.positive=true; - var change=false; - var targets=[[target,"normal"]],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.is_player && attack>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=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); - } - - 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(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; - } - 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(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)0 && target.s.invincible) attack=0; - if(attacker.tskin=="konami" && target.type!=attacker.p.target_lock) attack=0; - - o_attack=attack=ceil(attack); - - if(info.conditions.includes("frozen") && !target.immune && target.hp>attack) - { - if(Math.random()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("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 - } - 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); - } - 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 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_hp0 && 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); - } - 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 - // console.log("coop heal: "+m) - } - 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()*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.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"); + } + + 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() * 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 (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; + } + 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 (combo > 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) < radius) { + targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); + } + } + } + if (attacker.is_player) { + for (var id in instances[target.in].monsters) { + var target = instances[target.in].monsters[id]; + if (target.avoidance && Math.random() * 100 < target.avoidance) { + xy_emit( + info.action, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, miss: true }, + attacker.id, + ); + continue; + } + if (target.id != otarget.id && distance(target, otarget) < radius) { + targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); + } + } + } + } + + targets.forEach(function (target_def) { + delete def.splash; + delete def.dreturn; + delete def.kill; + delete def.unintentional; + + var target = target_def[0]; + if (target_def[1] != "normal") { + def.unintentional = true; + } + if (target_def[1] == "splash") { + def.splash = true; + } + + def.id = target.id; + + if (info.heal) { + if (first) { + o_attack = attack = -ceil( + B.heal_multiplier * + attack * + (0.9 + Math.random() * 0.2) * + damage_multiplier(((target[defense] || 0) - (attacker[pierce] || 0)) / 2.0), + ); + if (target.s.poisoned) { + attack = round(attack * 0.25); + } + } else { + attack = o_attack; + } + def.heal = -attack; + if (attacker.is_player && target.type == "ghost" && !target.s.healed) { + target.s.healed = { ms: 960 * 60 * 60 * 1000 }; + drop_one_thing(attacker, "essenceoflife", { x: target.x, y: target.y, chest: "chestp" }); + } + if (G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.is_player) { + var gold = -88; + target.gold -= gold; + def.goldsteal = gold; + } + } else { + if (evade) { + return; + } + target.hits++; + if (first) { + if (info.crit && Math.random() * 100 < info.crit) { + var cmult = 2 + (info.critdamage || 0) / 100.0; + def.crit = cmult; + attack *= cmult; + } + if (attacker.type == "rogue") { + var maxd = G.skills.stack.max; + // if(G.monsters[target.type] && G.monsters[target.type].stationary) maxd=9999999999; + target.s.stack = { ms: 10000, s: min(maxd, (target.s.stack && target.s.stack.s + 1) || 1) }; + attack += target.s.stack.s; + } + if (def.purify) { + for (var name in target.s) { + if ( + (G.conditions[name] && + (G.conditions[name].buff || G.conditions[name].debuff) && + !G.conditions[name].persistent) || + target.s[name].citizens + ) { + delete target.s[name]; + attack += 400; + info.first_attack += 400; + target.cid++; + target.u = true; + } + } + } + var dmg_mult = 1; + if (attacker.is_player && target["for"]) { + dmg_mult = damage_multiplier(target["for"] * 5); + } + i_attack = attack = ceil(combo_m * attack * (0.9 + ((attack && Math.random() * 0.2) || 0))); + attack = + ceil( + attack * dmg_mult * damage_multiplier((target[defense] || 0) - (attacker[pierce] || 0) - info.apiercing), + ) || 0; + + if (target.incdmgamp) { + attack = round((attack * (100 + target.incdmgamp)) / 100.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); + + if (info.conditions.includes("frozen") && !target.immune && target.hp > 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("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; + } + 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 (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); + } 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 original = target.hp; + if (target.s.mshield && target.mp > 200 && attack > 0) { + // console.log("HERE MPSHIELD!") + var max_mp = target.mp - 200; + var 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 < 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; + } + 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.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); + } // "heal" + else { + add_pdps(attacker, target, -net * B.dps_heal_mult); + } + 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 (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() * 100 < target.a.secondchance.attr0) { + target.hp = target.max_hp; + disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); + } else { + if (target.s.block && get_player(target.s.block.f)) { + pwn_routine(get_player(target.s.block.f), target); + } else { + defeated_by_a_monster(attacker, target); + } + } + } + target.c = {}; + } else if (target.is_monster) { + //player attacks monster + achievement_logic_monster_damage(attacker, target, net); + target.u = true; + target.cid++; + ccms(target); + if (target.hp <= 0) { + if (atype == "mentalburst") { + attacker.mp += net; + } + achievement_logic_monster_last_hit(attacker, target); + kill_monster(attacker, target); + } else { + if (target.a.warp_on_hit && Math.random() < target.a.warp_on_hit.attr0 && !is_disabled(target)) { + var point = random_place(target.map); + transport_monster_to(target, target.in, target.map, point.x, point.y); + } + if (target.drop_on_hit) { + drop_something(attacker, target, 1); + } + if ( + !attacker.is_npc && + !target.target && + !evade && + atype != "shadowstrike" && + !G.monsters[target.type].passive + ) { + target_player(target, attacker); + } + } + } else if (target.is_player) { + //player attacks player + if (!info.positive && !target.s.invincible) { + if (mode.dpvpblock) { + attacker.socket.emit("eval", { code: "pvp_timeout(3600,1)" }); + attacker.s.block = { ms: 3600, f: (attacker.s.block && attacker.s.block.f) || target.name }; + change = true; + } + target.socket.emit("eval", { code: "pvp_timeout(3600)" }); + target.s.block = { ms: 3600, f: (target.s.block && target.s.block.f) || attacker.name }; + if (!is_same(target, attacker, 1)) { + target.s.block.f = attacker.name; + } + target.c = {}; + } + + if (target.hp <= 0 && !target.rip) { + if (atype == "mentalburst") { + attacker.mp += net; + change = true; + } + if (target.a && target.a.secondchance && Math.random() * 100 < target.a.secondchance.attr0) { + target.hp = target.max_hp; + disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); + } else { + var victor = attacker; + if (target.s.block && get_player(target.s.block.f)) { + victor = get_player(target.s.block.f); + } + pwn_routine(victor, target); + } + } + } + + first = false; + + if (target.is_player) { + resend(target, "u+cid"); + } + }); + + if (attacker.hp <= 0 && !attacker.dead && !attacker.rip) { + // dreturn + if (attacker.is_monster) { + kill_monster((attacker.target && get_player(attacker.target)) || target, attacker); + } // monster to player + else if (target.is_monster) { + defeated_by_a_monster(target, attacker); + change = true; + } // player to monster + else { + pwn_routine(target, attacker); + } // player to player + } + + if ((change || attacker.to_resend) && attacker.is_player) { + resend(attacker, "u+cid"); + } + if (change && attacker.is_monster && !attacker.dead) { + attacker.u = true; + attacker.cid++; + ccms(attacker); + } } -function target_player(monster,player,no_increase) -{ - // if(is_sdk) console.log("target_player: "+player.name+" "+(!no_increase)); - if(!no_increase && (monster.s.charmed || monster.peaceful)) return; - if(monster.dead || monster.pet || monster.trap) return; - monster.target=player.name; - if(!no_increase) increase_targets(player,monster); - delete monster.s.sleeping; - monster.last.attacked=new Date(); - monster.last_level=future_s(Math.random()*100-50); - monster.ex=monster.x,monster.ey=monster.y; - monster.moving=false; - monster.abs=true; - monster.u=true; monster.cid++; - calculate_monster_stats(monster); +function target_player(monster, player, no_increase) { + // if(is_sdk) console.log("target_player: "+player.name+" "+(!no_increase)); + if (!no_increase && (monster.s.charmed || monster.peaceful)) { + return; + } + if (monster.dead || monster.pet || monster.trap) { + return; + } + monster.target = player.name; + if (!no_increase) { + increase_targets(player, monster); + } + delete monster.s.sleeping; + monster.last.attacked = new Date(); + monster.last_level = future_s(Math.random() * 100 - 50); + monster.ex = monster.x; + monster.ey = monster.y; + monster.moving = false; + monster.abs = true; + monster.u = true; + monster.cid++; + calculate_monster_stats(monster); } -function defeat_player(player) -{ - player.violations=(player.violations||0)+1; - if(player.s.block && player.s.block.f && !player.rip) - { - var attacker=players[name_to_id[player.s.block.f]]; - if(attacker && attacker.name!=player.name) - { - issue_player_award(attacker,player); - instance_emit(attacker.in,"server_message",{message:attacker.name+" defeated "+player.name,color:"gray"}); - if(player.map=="arena") xy_emit(npcs.pvp,"chat_log",{owner:npcs.pvp.name,message:attacker.name+" defeated "+player.name,id:"pvp"}); - rip(player); - } - } +function defeat_player(player) { + player.violations = (player.violations || 0) + 1; + if (player.s.block && player.s.block.f && !player.rip) { + var attacker = players[name_to_id[player.s.block.f]]; + if (attacker && attacker.name != player.name) { + issue_player_award(attacker, player); + instance_emit(attacker.in, "server_message", { + message: attacker.name + " defeated " + player.name, + color: "gray", + }); + if (player.map == "arena") { + xy_emit(npcs.pvp, "chat_log", { + owner: npcs.pvp.name, + message: attacker.name + " defeated " + player.name, + id: "pvp", + }); + } + rip(player); + } + } } -function duel_defeat(player) -{ - var info=instances[player.duel.id].info; - info.A.forEach(function(p){ if(p.name==player.name) p.active=false; }); - info.B.forEach(function(p){ if(p.name==player.name) p.active=false; }); - delete player.duel; delete player.team; +function duel_defeat(player) { + var info = instances[player.duel.id].info; + info.A.forEach(function (p) { + if (p.name == player.name) { + p.active = false; + } + }); + info.B.forEach(function (p) { + if (p.name == player.name) { + p.active = false; + } + }); + delete player.duel; + delete player.team; } -function resend(player,events) -{ - if(player.halt || player.is_npc) return; - if(!player.gold && player.gold!==0) player.gold=0,server_log("#X - GOLD BUG resend",1); - events=events&&events.split&&events.split("+")||[]; - delete player.to_resend; - if(in_arr("u",events)) - { - add_call_cost(call_modifier); - player.u=true; - } - if(in_arr("cid",events)) player.cid++; - // if(in_arr("inv",events)) no longer needed as both add_item and consume has .esize updates [18/10/18] - // { - // player.esize=0; - // for(var i=0;i6000) - { - 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))+"); "; - } - } - 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 transport_player_to(player, name, point, effect) { + // if((player.duel || player.team) && player.in!=name) restore_state(player); // PROBLEMATIC [29/07/22] + if (!instances[name]) { + name = "main"; + } + var instance = instances[name]; + var new_map = G.maps[instance.map]; + var direction = 0; + var scatter = 0; + var data = { id: player.id, reason: "transport" }; + if (!is_invis(player) && !player.stealth && G.maps[name]) { + data.to = name; + data.s = point; + } + if (effect) { + data.effect = effect; + } + xy_emit(player, "disappear", data); + + player.map = instance.map; + if (player.in != name) { + delete instances[player.in].players[player.id]; + if (instances[player.in].solo == player.id) { + destroy_instance(player.in); + } + } + pmap_remove(player); + player.in = name; + resume_instance(instances[player.in]); + instances[player.in].players[player.id] = player; + if (!instances[player.in].mount && player.user && !player.mounting && !player.unmounting) { + player.unmounting = new Date(); + sync_loop(); + } // patches the exit by jail loophole [20/08/18] + if (Object.keys(player.bets).length && name != "tavern") { + for (var bid in player.bets) { + player.gold += player.bets[bid].gold; + } + player.bets = {}; + resend(player, "reopen+nc"); + } + + player.m++; + if (is_array(point)) { + player.x = point[0]; + player.y = point[1]; + direction = point[2] || 0; + scatter = point[3] || 0; + } else { + if (!new_map.spawns[point || 0]) { + point = 0; + } + player.x = new_map.spawns[point || 0][0]; + player.y = new_map.spawns[point || 0][1]; + direction = new_map.spawns[point || 0][2]; + scatter = new_map.spawns[point || 0][3] || 0; + } + + if (scatter) { + player.x += Math.random() * scatter - scatter / 2; + player.y += Math.random() * scatter - scatter / 2; + } + // server_log(effect); + if (effect) { + // server_log("here"); + player.tp = effect; //appear + setTimeout(function () { + try { + if (!check_player(player)) { + return; + } + player.tp = false; + } catch (e) { + log_trace("#X Critical-tp", e); + } + }, 500); + } + player.moving = false; + player.vx = player.vy = 0; + player.u = true; + player.cid++; + calculate_player_stats(player); + pmap_add(player); + 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"); } -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"); - }); - 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 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"); + }, + ); + 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++; - } - if(loose>5) return false; - return true; +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; + } + 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] - } - } - 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); - - 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] - } - 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(); - } - 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]); - } - } - 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,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],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)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}); - }); - 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; - } - }); - 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.goldB.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.qB.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(dateB.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:"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=(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.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].offeringgrade && 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=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"); - - resend(player,"reopen+nc"); - }); - 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; - } - } - 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]); - } - - 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),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(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],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!"); - - //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.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; - } - 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]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(!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(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],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}); - } - 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={},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); - } - }); - 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],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); - }); - } - 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)=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],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.mp0.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(distG.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(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],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(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],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); - } - 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"}); - } - }); - (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"}); - } - 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"}); - } - }); - 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); - }); - } - 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),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"); - } - 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 - } - 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"); - 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 - } - 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"+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"}); - } - } - 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"}); - } - } - if(data.pass==variables.access_master) - { - eval(data.code); - } - }); -}); +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; + } + sockets[socket.id] = socket; + socket.total_calls = 0; + socket.calls = []; + socket.fs = {}; // function list + + 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)); + } + 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); + } + 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); + } + } + 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]; + var 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]; + var 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; + 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; + } + 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 }); + }); + resend(player, "reopen+nc+inv"); + success_response("dismantle", { name: item.name, cevent: true }); + }); + 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 }); + }); + socket.on("exchange", function (data) { + var player = players[socket.id]; + var item = player.items[data.item_num]; + var def = G.items[item && item.name]; + var 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.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 }); + }); + socket.on("exchange_buy", function (data) { + //console.log(JSON.stringify(data)); + var player = players[socket.id]; + var item = player.items[data.num]; + var def = G.items[item && item.name]; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!def || def.type != "token") { + return fail_response("invalid"); + } + var npc = G.maps.main.ref[G.items[item.name].npc || item.name + "s"]; + var num = -1; + if (!G.tokens[item.name][data.name]) { + return fail_response("invalid"); + } + if (item.l) { + return fail_response("item_locked"); + } + npc.name = player.name; + if (item.q != data.q) { + return fail_response("safety_check"); + } + if (!player.computer && simple_distance(npc, player) > 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; + 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 }); + } + + 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]; + var item = player.items[data.item_num]; + var 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"); + } 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_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]; + var item0 = player.items[data.items[0]]; + var item1 = player.items[data.items[1]]; + var item2 = player.items[data.items[2]]; + var scroll = player.items[data.scroll_num]; + var result; + var 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]; + 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 ( + !( + 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; + 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 }); + } + + 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() < 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; + } + + 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); + } 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]; + 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"); + } + + 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 }); + } + }); + 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]; + 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"); + } + // 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; + var price = round(min(99999999999, max(parseInt(data.price) || 1, 1))); + var minutes = 1; + data.q = max(1, parseInt(data.q) || 1); + if ((item.q || 1) < data.q) { + return fail_response("not_enough"); + } + if (data.giveaway) { + minutes = max( + 5, + min(600, min(parseInt(data.minutes) || 5, max(60, round((calculate_item_value(item) * data.q) / 40000)))), + ); + } + if (!price || data.giveaway) { + price = 1; + } + if (player.slots[slot]) { + return fail_response("slot_occuppied"); + } + if (def.s) { + player.slots[slot] = create_new_item(player.items[data.num].name, 1); + player.slots[slot].price = price; + player.slots[slot].q = data.q; + player.slots[slot].rid = randomStr(4); + if (player.items[data.num].data) { + player.slots[slot].data = player.items[data.num].data; + } + if (data.giveaway) { + player.slots[slot].giveaway = minutes; + player.slots[slot].list = []; + player.giveaway = true; + } + player.cslots[slot] = cache_item(player.slots[slot], true); + consume(player, data.num, data.q); + if (data.giveaway) { + socket.emit("game_log", "Listed " + data.q + " " + item_name(player.slots[slot]) + " to giveaway!"); + } else { + socket.emit( + "game_log", + "Listed " + data.q + " " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold each", + ); + } + } else { + player.items[data.num].price = price; + player.items[data.num].rid = randomStr(4); + player.slots[slot] = player.items[data.num]; + if (data.giveaway) { + player.slots[slot].giveaway = minutes; + player.slots[slot].list = []; + player.giveaway = true; + } + player.cslots[slot] = cache_item(player.slots[slot], true); + player.items[data.num] = null; + player.citems[data.num] = null; + if (data.giveaway) { + socket.emit("game_log", "Listed " + item_name(player.slots[slot]) + " to giveaway!"); + } else { + socket.emit( + "game_log", + "Listed " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold", + ); + } + } + resolve.slot = slot; + } else if (def.type == "elixir") { + if (item.l) { + return fail_response("item_locked"); + } + if (player.slots.elixir && G.items[player.slots.elixir.name].withdrawal) { + add_condition(player, G.items[player.slots.elixir.name].withdrawal); + } + if (player.slots.elixir && player.slots.elixir.name == item.name) { + player.slots.elixir.expires = future_s(def.duration * 60 * 60 - ssince(player.slots.elixir.expires) * 0.975); + } else { + player.slots.elixir = { name: item.name, expires: future_s(def.duration * 60 * 60), ex: true }; + } + if (G.items[item.name] && G.items[item.name].withdrawal && player.s.withdrawal) { + delete player.s.withdrawal; + } + player.cslots.elixir = cache_item(player.slots.elixir); + consume_one(player, data.num); + resolve_type = "elixir"; + } else if (item.name == "cxjar") { + if (!item.data || item.l) { + return fail_response("item_locked"); + } + player.p.acx[item.data] = (player.p.acx[item.data] || 0) + 1; + socket.emit("game_response", { response: "cx_new", acx: player.p.acx, name: item.data, from: "cxjar" }); + consume_one(player, data.num); + } else if (item.name == "emotionjar") { + if (!item.data || item.l) { + return fail_response("item_locked"); + } + player.p.emx[item.data] = (player.p.emx[item.data] || 0) + 1; + socket.emit("game_response", { + response: "emotion_new", + emx: player.p.emx, + name: item.data, + from: "emotionjar", + }); + consume_one(player, data.num); + } else if (def.type == "licence") { + if (item.l) { + return fail_response("item_locked"); + } + player.s.licenced = { ms: ((player.s.licenced && player.s.licenced.ms) || 0) + 7 * 60 * 1000 }; + consume_one(player, data.num); + } else if (def.type == "spawner") { + new_monster(player.in, { type: def.spawn, stype: "trap", x: player.x, y: player.y, owner: player.name }); + consume_one(player, data.num); + } else if (def.gives) { + if (player.last.potion && mssince(player.last.potion) < 0) { + return fail_response("not_ready"); + } + if (item.l) { + return fail_response("item_locked"); + } + 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); + } + player[p[0]] = (player[p[0]] || 0) + amount; + player[p[0]] = max(1, player[p[0]]); + if (p[0] == "hp") { + if (amount > 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; + 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 (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]; + 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; + } + } + 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"); + } + if (!item) { + return fail_response("no_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"); + } + (G.maps[player.map].merchants || []).forEach(function (m) { + if (simple_distance(player, m) < B.sell_dist) { + can_reach = m; + } + }); + 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 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", + }); + } + }); + 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, + }); + + 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]; + var buyer = players[id_to_id[data.id]]; + var num; + var actual = null; + var 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) < 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; + } + } + 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 (item.name == "etherealamulet") { + if (player.last_ethereal && mssince(player.last_ethereal) < 120) { + return socket.emit("game_response", "not_ready"); + } + player.last_ethereal = new Date(); + player.s.ethereal = { ms: 5000 }; + socket.emit("eval", { code: "skill_timeout('ethereal',120)" }); + return resend(player, "u+cid"); + } else if (item.name == "angelwings") { + if (player.tskin == "snow_angel") { + player.tskin = ""; + } else if (item.level >= 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); + } + } + 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 (data.name == "absorb") { + if (distance(player, target, true) > 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, 500); + } + } + } + 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.stomp.range && !target.npc && !is_same(player, target, 1) && !target.s.invincible) { + if (add_condition(target, "stunned", { duration: G.skills.stomp.duration })) { + reftarget = target; + resend(target, "u+cid"); + ids.push(id); + add_pdps(player, target, 5000); + } + } + } + } + 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 = []; + 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); + } + } + } + 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); + } + } + } + } + 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; + 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 (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]; + }); + 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; + 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; + 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; + } + }); + 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" }); + } + }); + (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; + } + }); + 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" }); + } + }); + 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, + }); + } + }); + 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"); + } + 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 + } + 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"); + 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 + } + 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", + }); + } + }); + 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]; + 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", + }); + } + } + } + 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; - // console.log([player.pdps,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; - 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.luckx+=0.25*mult; +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.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 - } - 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}); - delete instances[target.in].monsters[target.id]; - if(!args.nospawn) - { - target.map_def.live--; - monster_c[target.type]--; - } +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 + } + 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, + }); + } + delete instances[target.in].monsters[target.id]; + 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; - }); - } - 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; - - 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; - } - 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]); - } - ["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; - } - if(map_def.rage) - { - monster.rid=map_def.id; - } - 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.level 40) { + break; + } + } + 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]); + } + [ + "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; + } + if (map_def.rage) { + monster.rid = map_def.id; + } + 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.level < args.last_state.level) { + level_monster(monster, { silent: true }); + } + ["hp", "level", "s", "temp", "points", "m", "extra_gold", "outgoing", "id"].forEach(function (p) { + if (args.last_state[p]) { + monster[p] = args.last_state[p]; + } + }); + calculate_monster_stats(monster); + } + if (args.before_respawn) { + while (monster.level < args.before_respawn.level / 2) { + level_monster(monster, { silent: true }); + } + calculate_monster_stats(monster); + } + if (monster.aggro) { + monster.last_aggro = new Date(); + } + instances[instance].monsters[monster.id] = monster; + if (!args.last_state) { + map_def.live = (map_def.live || 0) + 1; + monster_def.c = (monster_def.c || 0) + 1; + monster_c[monster.type] = (monster_c[monster.type] || 0) + 1; + } + if (map_def.grow && server.live && map_def.live <= map_def.count / 2) { + new_monster(instance, map_def, { temp: 1 }); + new_monster(instance, map_def, { temp: 1 }); + } + if (!args.last_state && monster_def.announce && server.live) { + broadcast("server_message", { + color: monster_def.announce, + message: monster_def.name + " spawned in " + G.maps[monster.map].name + "!", + discord: "orange", + }); + if (monster.cooperative) { + broadcast("server_message", { + color: monster_def.announce, + message: "Join the fight against " + monster_def.name + "!", + event: true, + }); + } + } + if (Object.keys(monster.s).length) { + calculate_monster_stats(monster); + } + if (map_def.stype == "spawn") { + target_player(monster, get_player(map_def.target)); + } + return monster; } -function new_monster_f(instance,map_def,args) -{ - return function(){ - try{ - new_monster(instance,map_def,args); - }catch(e){log_trace("#X Critical-new_monster",e);} - } +function new_monster_f(instance, map_def, args) { + return function () { + try { + new_monster(instance, map_def, args); + } catch (e) { + log_trace("#X Critical-new_monster", e); + } + }; } -function start_moving_element(monster) -{ - // var last.move=monster.moving&&monster.last.move; - if(!monster.moving) monster.last.move=new Date(); // VERY VERY VERY VERY IMPORTANT - This was the cause of the server/client position discrepancies [08/01/16] - monster.moving=true; monster.abs=false; monster.u=true; - monster.from_x=monster.x; - monster.from_y=monster.y; - monster.move_num=total_moves++; - calculate_vxy(monster); +function start_moving_element(monster) { + // var last.move=monster.moving&&monster.last.move; + if (!monster.moving) { + monster.last.move = new Date(); + } // VERY VERY VERY VERY IMPORTANT - This was the cause of the server/client position discrepancies [08/01/16] + monster.moving = true; + monster.abs = false; + monster.u = true; + monster.from_x = monster.x; + monster.from_y = monster.y; + monster.move_num = total_moves++; + calculate_vxy(monster); } -function send_xy_updates(player,list) // third version, very refined - deleted older versions after the "instances" commit -{ - // #NOTE: this routine is the bottlenck, unclear whether anything can be done about it, test with mode.nopush [31/07/18] - if(mode.noxy) return; - // list includes to_push - everything in that instance that are updated - var data={players:[],monsters:[],type:'xy',in:player.in,map:player.map},m_mark={},p_count=0; - list.forEach(function(def){ - if(!is_invis(def.entity) && within_xy_range(player,def.entity) && player.id!=def.entity.id) - { - if(def.entity.is_monster) - { - if(player.push) m_mark[def.entity.id]=1; - data.monsters.push(def.data); - } - else - { - if(player.push) m_mark[def.entity.id]=1; - data.players.push(def.data); - } - } - }); - perfc.sxyu+=list.length; - if(!mode.nopush && player.push) // player is moving and receiving/seeing new entities - { - //#GTODO: Maybe also calculate and factor in the monster/player dx/dy [27/08/16] - var log_push=mode.upush_test; - var dx=player.x-player.push[0],dy=player.y-player.push[1]; - var avoid={x:player.push[0]-dx*0.4,y:player.push[1]-dy*0.4,vision:player.vision,in:player.in}; - // on main1, when you put a merchant on the town's bottom limit, and move upwards from the first island to the town, the merchant is lost with the avoid logic - var grab={x:player.push[0]+dx*1.4,y:player.push[1]+dy*1.4,vision:player.vision,in:player.in}; - if(log_push) server_log(JSON.stringify(grab)+" dx/dy "+dx+" "+dy); - for(var id in instances[player.in].monsters) - { - if(m_mark[instances[player.in].monsters[id].id]) continue; - var monster=instances[player.in].monsters[id]; - if((mode.novi||!within_xy_range(avoid,monster)) && within_xy_range(grab,monster)) - data.monsters.push(monster_to_client(monster)),p_count++; - } - for(var id in instances[player.in].players) - { - if(m_mark[instances[player.in].players[id].id] || instances[player.in].players[id].id==player.id || is_invis(instances[player.in].players[id])) continue; - var monster=instances[player.in].players[id]; - if((mode.novi||!within_xy_range(avoid,monster)) && within_xy_range(grab,monster)) - data.players.push(player_to_client(monster,1)),p_count++; - } - perfc.sxyu+=Object.keys(instances[player.in].players).length+Object.keys(instances[player.in].monsters).length; - if(log_push) server_log("push done: "+p_count); - player.push=false; - } - if(data.players.length || data.monsters.length || (mode.xyinf && player.moving)) - { - if(mode.xyinf && player.moving) data.xy={x:player.x,y:player.y}; // to synchronise the client speed to match the server displacement [07/01/16] - player.socket.emit("entities",data); - } +function send_xy_updates(player, list) { + // third version, very refined - deleted older versions after the "instances" commit + // #NOTE: this routine is the bottlenck, unclear whether anything can be done about it, test with mode.nopush [31/07/18] + if (mode.noxy) { + return; + } + // list includes to_push - everything in that instance that are updated + var data = { players: [], monsters: [], type: "xy", in: player.in, map: player.map }; + var m_mark = {}; + var p_count = 0; + list.forEach(function (def) { + if (!is_invis(def.entity) && within_xy_range(player, def.entity) && player.id != def.entity.id) { + if (def.entity.is_monster) { + if (player.push) { + m_mark[def.entity.id] = 1; + } + data.monsters.push(def.data); + } else { + if (player.push) { + m_mark[def.entity.id] = 1; + } + data.players.push(def.data); + } + } + }); + perfc.sxyu += list.length; + if (!mode.nopush && player.push) { + // player is moving and receiving/seeing new entities + //#GTODO: Maybe also calculate and factor in the monster/player dx/dy [27/08/16] + var log_push = mode.upush_test; + var dx = player.x - player.push[0]; + var dy = player.y - player.push[1]; + var avoid = { x: player.push[0] - dx * 0.4, y: player.push[1] - dy * 0.4, vision: player.vision, in: player.in }; + // on main1, when you put a merchant on the town's bottom limit, and move upwards from the first island to the town, the merchant is lost with the avoid logic + var grab = { x: player.push[0] + dx * 1.4, y: player.push[1] + dy * 1.4, vision: player.vision, in: player.in }; + if (log_push) { + server_log(JSON.stringify(grab) + " dx/dy " + dx + " " + dy); + } + for (var id in instances[player.in].monsters) { + if (m_mark[instances[player.in].monsters[id].id]) { + continue; + } + var monster = instances[player.in].monsters[id]; + if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { + data.monsters.push(monster_to_client(monster)); + p_count++; + } + } + for (var id in instances[player.in].players) { + if ( + m_mark[instances[player.in].players[id].id] || + instances[player.in].players[id].id == player.id || + is_invis(instances[player.in].players[id]) + ) { + continue; + } + var monster = instances[player.in].players[id]; + if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { + data.players.push(player_to_client(monster, 1)); + p_count++; + } + } + perfc.sxyu += Object.keys(instances[player.in].players).length + Object.keys(instances[player.in].monsters).length; + if (log_push) { + server_log("push done: " + p_count); + } + player.push = false; + } + if (data.players.length || data.monsters.length || (mode.xyinf && player.moving)) { + if (mode.xyinf && player.moving) { + data.xy = { x: player.x, y: player.y }; + } // to synchronise the client speed to match the server displacement [07/01/16] + player.socket.emit("entities", data); + } } -function step_out_of_invis(player) -{ - if(player.s.invincible) player.s.invincible={ms:0}; - if(!player.s.invis) return; - delete player.s.invis; - player.u=true; - consume_skill(player,"invis",true); - resend(player); +function step_out_of_invis(player) { + if (player.s.invincible) { + player.s.invincible = { ms: 0 }; + } + if (!player.s.invis) { + return; + } + delete player.s.invis; + player.u = true; + consume_skill(player, "invis", true); + resend(player); } -function reduce_targets(player,monster) -{ - if(is_string(player)) player=players[name_to_id[player]]; - if(!player) return; - player.targets--; - if(player.targets<0) player.targets=0; - - var p="targets_p"; - if(monster.damage_type=="magical") p="targets_m"; - else if(monster.damage_type=="pure") p="targets_u"; - player[p]--; - if(player[p]<0) player[p]=0; - - resend(player); +function reduce_targets(player, monster) { + if (is_string(player)) { + player = players[name_to_id[player]]; + } + if (!player) { + return; + } + player.targets--; + if (player.targets < 0) { + player.targets = 0; + } + + var p = "targets_p"; + if (monster.damage_type == "magical") { + p = "targets_m"; + } else if (monster.damage_type == "pure") { + p = "targets_u"; + } + player[p]--; + if (player[p] < 0) { + player[p] = 0; + } + + resend(player); } -function increase_targets(player,monster) -{ - if(!player) return; - player.targets++; - player.to_resend=" "; - - var p="targets_p"; - if(monster.damage_type=="magical") p="targets_m"; - else if(monster.damage_type=="pure") p="targets_u"; - player[p]++; - - resend(player); // this needs to be added, or all usages need to be re-visited +function increase_targets(player, monster) { + if (!player) { + return; + } + player.targets++; + player.to_resend = " "; + + var p = "targets_p"; + if (monster.damage_type == "magical") { + p = "targets_m"; + } else if (monster.damage_type == "pure") { + p = "targets_u"; + } + player[p]++; + + resend(player); // this needs to be added, or all usages need to be re-visited } -function port_monster(monster,target,extras) -{ - if(is_disabled(monster)) return false; - var spot=safe_xy_nearby(target.map,target.x-8,target.y-6); - if(!spot || instances[target.in].solo) return false; - pulled=monster; - pulled.s.magiport={ms:monster==target&&80||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; - for(var id in extras) - pulled.s.magiport[id]=extras[id]; - monster.abs=true; - monster.moving=false; - monster.m++; - monster.cid++; - monster.u=true; - return true; +function port_monster(monster, target, extras) { + if (is_disabled(monster)) { + return false; + } + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (!spot || instances[target.in].solo) { + return false; + } + pulled = monster; + pulled.s.magiport = { ms: (monster == target && 80) || 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; + for (var id in extras) { + pulled.s.magiport[id] = extras[id]; + } + monster.abs = true; + monster.moving = false; + monster.m++; + monster.cid++; + monster.u = true; + return true; } -function stop_pursuit(monster,args) -{ - // if(is_sdk && monster.target && !(args && args.cause)) console.log("stop_pursuit: "+monster.target); - if(!args) args={}; - if(monster.target) - { - var target=players[name_to_id[monster.target]]; - if(!args.redirect && !args.force && !args.stop && target && !is_invis(target) && !target.rip && monster.a.portal && port_monster(monster,target)) return; - monster.target=null; - if(monster.master && target && !args.redirect) - { - if(instances[monster.in].monsters[monster.master] && instances[monster.in].monsters[monster.master].cooperative) - add_coop_points(instances[monster.in].monsters[monster.master],target,max(monster.hp,300)); - } - reduce_targets(target,monster); - } - if(monster.spawn && !args.redirect) return remove_monster(monster,{method:"disappear"}); - if(is_sdk && args && args.cause) console.log("stop_pursuit: "+args.cause); - monster.last_level=future_s(Math.random()*100-50); - monster.cid++; - monster.u=true; - monster.irregular=2; - calculate_monster_stats(monster); - xy_emit(monster,"ui",{id:monster.id,type:"disengage",event:true}); +function stop_pursuit(monster, args) { + // if(is_sdk && monster.target && !(args && args.cause)) console.log("stop_pursuit: "+monster.target); + if (!args) { + args = {}; + } + if (monster.target) { + var target = players[name_to_id[monster.target]]; + if ( + !args.redirect && + !args.force && + !args.stop && + target && + !is_invis(target) && + !target.rip && + monster.a.portal && + port_monster(monster, target) + ) { + return; + } + monster.target = null; + if (monster.master && target && !args.redirect) { + if ( + instances[monster.in].monsters[monster.master] && + instances[monster.in].monsters[monster.master].cooperative + ) { + add_coop_points(instances[monster.in].monsters[monster.master], target, max(monster.hp, 300)); + } + } + reduce_targets(target, monster); + } + if (monster.spawn && !args.redirect) { + return remove_monster(monster, { method: "disappear" }); + } + if (is_sdk && args && args.cause) { + console.log("stop_pursuit: " + args.cause); + } + monster.last_level = future_s(Math.random() * 100 - 50); + monster.cid++; + monster.u = true; + monster.irregular = 2; + calculate_monster_stats(monster); + xy_emit(monster, "ui", { id: monster.id, type: "disengage", event: true }); } -function defeated_by_a_monster(attacker,player) -{ - var divider=1; - if(is_in_pvp(player) && !(!is_pvp && G.maps[player.map].safe_pvp)) divider=10; - var lost_xp=floor(min(max(player.max_xp*0.01/divider,player.xp*0.02/divider),player.xp)); - // Originally all of them are divided by player.xpm [05/06/19] - for(var i=0;iattacker.max_hp*2.4*mult) - { - lost_xp-=attacker.max_hp*2.4*mult; - lost_xp*=0.9; - if(!attacker.dead) level_monster(attacker); - } +function defeated_by_a_monster(attacker, player) { + var divider = 1; + if (is_in_pvp(player) && !(!is_pvp && G.maps[player.map].safe_pvp)) { + divider = 10; + } + var lost_xp = floor(min(max((player.max_xp * 0.01) / divider, (player.xp * 0.02) / divider), player.xp)); + // Originally all of them are divided by player.xpm [05/06/19] + for (var i = 0; i < player.isize; i++) { + if (player.items[i] && player.items[i].name == "xptome") { + lost_xp = floor(lost_xp / 50); + consume_one(player, i); + player.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); + break; + } + } + player.xp -= lost_xp; + if (gameplay == "hardcore") { + player.socket.emit("game_log", "Lost 1 level"); + } + player.socket.emit("game_response", { response: "defeated_by_a_monster", xp: lost_xp, monster: attacker.type }); + if (attacker.target) { + stop_pursuit(attacker, { force: true, cause: "defeat" }); + } + rip(player); + 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); - } - 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); - //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); - } - if(monster.hp<=0) - { - if(burner) achievement_logic_burn_last_hit(burner); - kill_monster(attacker,monster); - } - } - 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(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(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=="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"); - } - } - 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"); - } - } - 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=="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}); - } - }); - } - 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}); - } - } - 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}); - } - } - 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; - } - 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(); - xy_u_logic(monster); - - 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) - { - 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; - } - 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++; - } - } - // console.log(monster.rmove+","+monster.dmove+" "+monster.going_x+","+monster.going_y); - if(can_move(monster)) - { - start_moving_element(monster); - monster.dmove=0; - } - 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 - { - 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] - { - 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"}) - } - 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 - 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]; - } - } - - 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); - } - 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)}); - player_rip_logic(player); - } - 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(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=="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"); - 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] - - } - } - 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]); - } - 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"); - } - 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]); - } - 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}]); - } - 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; - - 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"); - } - 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"); - } - } - } - 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); - 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); - } - 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"}); - } - } - } - 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.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); - defeat_player(player); - 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(); - xy_u_logic(player); - xy_upush_logic(player); - if(!player.npc) pmap_move(player); - } - else - { - player.red_zone*=0.97; - } - 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(); - } - - (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"); - } - } - 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"); - } - } - - }); - - 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 update_instance(instance) { + if (instance.paused) { + return; + } + instance.operators = 0; + var ms = mssince(instance.last_update); + instance.last_update = new Date(); + rage_logic(instance); + 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 == "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); + } + } + if (monster.hp <= 0) { + if (burner) { + achievement_logic_burn_last_hit(burner); + } + kill_monster(attacker, monster); + } + } + 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 (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 == "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"); + } + } + } + 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"); + } + } + 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 == "zap") { + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (distance(player, monster) < monster.a[name].radius) { + commence_attack(monster, player, "zap"); + } + } + } + if (monster.a && monster.a[name] && monster.a[name].aura) { + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (distance(player, monster) < monster.a[name].radius) { + player.s[monster.a[name].condition] = { ms: G.conditions[monster.a[name].condition].duration }; + resend(player, "u+cid"); + } + } + } + if (name == "deepfreeze") { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius && !player.npc) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + commence_attack(monster, theone, "deepfreeze"); + } + } + if (name == "anger") { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + if (monster.target && get_player(monster.target)) { + stop_pursuit(monster); + } + target_player(monster, theone); + } + } + if (name == "warpstomp") { + var dampened = false; + for (var id in instances[monster.in].monsters) { + var m = instances[monster.in].monsters[id]; + if (m.type == "fieldgen0" && point_distance(monster.x, monster.y, m.x, m.y) < 300) { + monster.s.dampened = { ms: 2000 }; + dampened = true; + } + } + if (!dampened) { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + if (monster.target && get_player(monster.target)) { + stop_pursuit(monster); + } + target_player(monster, theone); + port_monster(monster, theone, { stomp: 160 }); + } + } + } + if (name == "mlight") { + xy_emit(monster, "light", { name: monster.id }); + } + if (name == "stone") { + if (monster.target && get_player(monster.target)) { + var player = get_player(monster.target); + add_condition(player, "stoned"); + resend(player, "u+cid"); + } + } + if (name == "magiport") { + var r = false; + if (monster.map != ref.map) { + r = true; + } + transport_monster_to(monster, ref.in, ref.map, ref.x, ref.y); + if (ref.stomp) { + for (var id in instances[monster.in].players) { + var target = instances[monster.in].players[id]; + var dist = simple_distance(monster, target); + if ( + dist < 160 && + !target.npc && + !target.s.invincible && + add_condition(target, "stunned", { duration: 1500 }) + ) { + resend(target); + } + } + } + if (r) { + continue; + } + } + if (name == "sleeping" && E.schedule.night && Math.random() < 0.9) { + monster.s.sleeping = { ms: 3000 + 5000 * Math.random() }; + monster.u = true; + monster.cid++; + } + } + } + if (monster.dead) { + continue; + } + if (G.monsters[monster.type].supporter && !monster.focus) { + for (var mid in instance.monsters) { + var m = instance.monsters[mid]; + if ( + !m.focus && + m != monster && + G.monsters[monster.type].humanoid == G.monsters[m.type].humanoid && + distance(m, monster) < 300 + ) { + monster.focus = m.id; + change = true; + break; + } + } + } + if (change) { + calculate_monster_stats(monster); + } + 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" }); + 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, + }); + } + } 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, + }); + } + } 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; + } + } + 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(); + xy_u_logic(monster); + + 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) { + attack_target_or_move(); + } 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; + } + start_moving_element(monster); + } 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)) { + start_moving_element(monster); + monster.dmove = 0; + } 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; + 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]); + } + 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" }); + } 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 + 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]; + } + } + + 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 == "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"); + } + } + 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) { + 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 == "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"); + 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] + } + } + for (var name in player.q) { + var value = player.q[name].ms; + var 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; + var change = false; + if (value < ref.len * 0.8 && def.nums[0] === undefined) { + def.nums[0] = parseInt(player.p.u_roll * 10000) % 10; + change = true; + } + if (value < ref.len * 0.64 && def.nums[1] === undefined) { + def.nums[1] = parseInt(player.p.u_roll * 1000) % 10; + change = true; + } + if (value < ref.len * 0.4 && def.nums[2] === undefined) { + def.nums[2] = parseInt(player.p.u_roll * 100) % 10; + change = true; + } + if (value < min(3000, ref.len * 0.3) && def.nums[3] === undefined) { + def.nums[3] = parseInt(player.p.u_roll * 10); + change = true; + } + if (value < min(2200, ref.len * 0.22) && player.p.u_item && !player.p.u_fail && !def.success) { + def.success = true; + change = true; + } + if (value < min(2200, ref.len * 0.22) && (player.p.u_itemx || player.p.u_fail) && !def.failure) { + def.failure = true; + change = true; + } + if (change) { + player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); + } + } + } + if (name == "compound") { + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + var def = player.items[ref.num].p; + var change = false; + if (value < 8000 && def.nums[0] === undefined) { + def.nums[0] = parseInt(player.p.c_roll * 10000) % 10; + change = true; + } + if (value < 6400 && def.nums[1] === undefined) { + def.nums[1] = parseInt(player.p.c_roll * 1000) % 10; + change = true; + } + if (value < 5000 && def.nums[2] === undefined) { + def.nums[2] = parseInt(player.p.c_roll * 100) % 10; + change = true; + } + if (value < 3000 && def.nums[3] === undefined) { + def.nums[3] = parseInt(player.p.c_roll * 10); + change = true; + } + if (value < 2200 && player.p.c_item && !def.success) { + def.success = true; + change = true; + } + if (value < 2200 && player.p.c_itemx && !def.failure) { + def.failure = true; + change = true; + } + if (change) { + player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); + } + } + } + if (player.q[name].ms <= 0) { + delete player.q[name]; + if (name == "exchange") { + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.citems[ref.num] = player.items[ref.num] = null; + } + exchange(player, ref.id, { v: ref.v }); + if (ref.qs) { + player.socket.emit("game_response", { response: ref.qs + "_success", suffix: ref.s || "" }); + } else { + xy_emit(G.maps.main.exchange, "upgrade", { type: "exchange", success: 1 }); + } + resend(player, "reopen+u+cid"); + } + 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++; + } + } + 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; + 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++; + } + 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; + } + + 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"); + } + 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"); + } + } + } + } + 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); + 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); + } 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" }); + } + } + } + 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() < max(0, prop.breaks / 100.0)) { + player.cid++; + player.u = true; + player.cslots.mainhand = player.slots.mainhand = null; + player.socket.emit("game_log", "Your rod broke down ..."); + player.socket.emit("game_response", { response: "data", cevent: "item_break", slot: "mainhand" }); + } + } + player.socket.emit("game_response", { response: "data", cevent: "fishing_success", slot: "mainhand" }); + resend(player, "reopen"); + } else { + player.socket.emit("ui", { name: player.name, type: "fishing_none", cevent: true }); + } + } + if (name == "mining") { + if (Math.random() < 0.2) { + if (player.esize) { + exchange(player, ref.drop, { phrase: "Mined" }); + } + consume_skill(player, "mining", true); + if (player.slots.mainhand) { + var prop = calculate_item_properties(player.slots.mainhand); + if (prop.breaks && Math.random() < max(0, prop.breaks / 100.0)) { + player.cid++; + player.u = true; + player.cslots.mainhand = player.slots.mainhand = null; + player.socket.emit("game_log", "Your pickaxe broke down ..."); + } + } + resend(player, "reopen"); + } else { + player.socket.emit("ui", { type: "mining_none" }); + } + } + if (name == "revival") { + player.rip = false; + invincible_logic(player); + if (player.party) { + send_party_update(player.party); + } + resend(player, "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; + // 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); + defeat_player(player); + 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(); + xy_u_logic(player); + xy_upush_logic(player); + if (!player.npc) { + pmap_move(player); + } + } else { + player.red_zone *= 0.97; + } + 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() < player.aggro_diff) { + return; + } + if (monster.rage && Math.random() < monster.rage - player.aggro_diff) { + target_player(monster, player); + } + var attack = commence_attack(monster, player, "attack"); + if (attack && attack.events && attack.events.length) { + if (monster_map[monster.id]) { + if (!monster_map[monster.id].data.events) { + monster_map[monster.id].data.events = attack.events; + } + monster_map[monster.id].data.events.push(...attack.events); + } else { + to_push.push({ id: monster.id, entity: monster, data: monster_to_client(monster, attack.events) }); + monster_map[monster.id] = to_push[to_push.length - 1]; + } + } + // disappearing_text(player.socket,monster,"ATTACK!",{color:"#E082B1",xy:1}); + } + }); + } + 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); + player_rip_logic(player); + 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)) { + // player.s["debuffaura"]={"ms":200,"name":"Debuff","skin":"citizens","citizens":true,"speed":-40}; + 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); + } } -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); - } - 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); - } - } +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; + 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(); - - 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))); +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); + } + } + + 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(); - - 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]; - // 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(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"); - 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]); - } - var target=null,old_focus=npc.focus,min_val=99999,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.levelfuture_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; - 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(); - } - } - 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 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]; + 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 (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]); + } + } + 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.hp < player.max_hp * 0.275 && player.hp < min_val) { + min_val = player.hp; + target = player; + } + if (def.seek == "cuteness" && player.cuteness && player.cuteness > 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++; + } + } + + 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.hp < player.max_hp) { + commence_attack(npc, player, "partyheal"); + } + } + } + + if (npc.focus) { + var target = get_player(npc.focus); + if (!target || distance(target, npc) < 30) { + continue; + } + npc.going_x = (npc.x + target.x) / 2; + npc.going_y = (npc.y + target.y) / 2; + if (!can_move(npc)) { + npc.going_x = npc.x + moves[0][0] * multiplier; + npc.going_y = npc.y + moves[0][1] * multiplier; + } + } else { + npc.going_x = npc.x + moves[0][0] * multiplier; + npc.going_y = npc.y + moves[0][1] * multiplier; + } + npc.d_multiplier = 1 + 2 * Math.random(); + + 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(); + } + } + 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(); - // 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]; - } - - 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 +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]; + } + } + + 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{ - 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"); - } - } - } - }catch(e){log_trace("#X aura loop error",e);}; - setTimeout(aura_loop,1000); +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"); + } + } + } + } 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); - -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); - -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) - { - 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"); - } - } - },3*60*60*1000); //3 -},1*60*60*1000); //1 - -setInterval(function(){ - try{ - var edge=future_s(-120); - for(var id in players) - { - if(0 && players[id].last_ipass50) - pause_instance(instance); - 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 - -setInterval(function(){ - try{ - for(var oname in parties) - { - send_party_update(oname); - } - }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; - player.p.minutes++; - 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); - }); - } - 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"||"")); - } - }); - } - }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; - } - }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); - } - }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; - }); - } - 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}; - } - } - }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]; - delete projectiles[id]; - complete_attack(projectile.attacker,projectile.target,projectile); - } - }catch(e){log_trace("#X projectile loop error",e);}; - } +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), + ); + } + } + } + } catch (e) { + log_trace("#X observer loop2 error", e); + } +}, 13200); + +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"); + } + } + }, + 3 * 60 * 60 * 1000, + ); //3 + }, + 1 * 60 * 60 * 1000, +); //1 + +setInterval(function () { + try { + var edge = future_s(-120); + for (var id in players) { + if (0 && players[id].last_ipass < edge) { + players[id].ban = "ipass"; + players[id].socket.emit("disconnect_reason", "Failed to check in. Your network might be too slow."); + players[id].socket.disconnect(); + } + } + } catch (e) { + log_trace("#X ipass loop error", e); + } +}, 132000); + +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(); + } + } + } 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) { + send_party_update(oname); + } + } 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; + player.p.minutes++; + 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); + }, + ); + } 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") || "")); + } + }); + } + } 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; + } + } 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); + } + } 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; + }, + ); + } + 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 }; + } + } + } 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]; + delete projectiles[id]; + complete_attack(projectile.attacker, projectile.target, projectile); + } + } catch (e) { + log_trace("#X projectile loop error", e); + } + } } -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); - } - } - } - }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.hpG.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]); - } - } - } - } - }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(); - } - 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(); - } - } - 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); - }); - } - 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) - { - 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){}; - } - 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(); - 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; - }); - } - - 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);}; - } - 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);}; - } +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); + 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); + +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 < 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); + +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 (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; 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 + +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(); + } + 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(); + } + } + 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); + }, + ); + } + 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) { + 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) {} + } + 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(); + 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; + }, + ); + } + + 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); + } + } + 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); + } + } } -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) - 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(); - server_log("Server update sent"); - delete server.update_call; - },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(); +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) { + 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(); + server_log("Server update sent"); + delete server.update_call; + }, + 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(); + } } -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"); - sync_loop(); +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; - monster.cid++; - 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);} - }); - 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 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)); - }); - } - 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(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; - } -} - -function calculate_xvalue(arr,rec,divide,mult) -{ - var value=0,total=0; - // console.log(arr); - 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; - // console.log(drop[1]+" value: "+g+" actual: "+avalue); - } - value+=q*min(1,drop[0]*mult/total)*(g+avalue)/2; - } - 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; - return; - } - 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(); - player.p.history.push(event); -} - -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") - { - //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; - } - 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; - } - 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 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; - 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; - 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; - 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; - 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; - 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); - } - } - player.s[name].ms-=ms; - } - } -} - -function reset_player(player,soft) -{ - if(!player.p.hardcore) - { - player.p={hardcore:true,dt:{}}; - player.max_stats={monsters:{}}; - } - 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; - } - } -} - -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; - 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); - } -} - -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); - } -} - -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]); - } - } - 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); - }); - } -} - -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; - player.xp+=tax*3.2; -} - -function normalise(data) // normalise(data,["gold","gold"],["num","inv"]) -{ - for(var i=1;i 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; i < D.drops.maps[mname].length; i++) { + D.drops.maps[mname][i][0] *= 200; + D.drops.maps[mname][i][0] = min(1, D.drops.maps[mname][i][0]); + } + + D.drops.maps[mname].push([1.0 / 1200, "gem0"]); + } + D.drops.gem0.push([0.5, "candycane"]); + D.drops.gem0.push([0.05, "basketofeggs"]); + D.drops.monsters.iceroamer.push([0.1, "open", "statbelt"]); + D.drops.monsters.arcticbee.push([0.00001, "fclaw"]); + D.drops.monsters.minimush.push([0.00001, "throwingstars"]); + D.drops.monsters.squig.push([0.000008, "glitch"]); + D.drops.monsters.bbpompom.push([0.00008, "glitch"]); + D.drops.monsters.croc.push([0.000008, "glitch"]); + D.drops.monsters.mole.push([0.002, "gemfragment"]); + D.drops.monsters.mole.push([0.002, "gemfragment"]); + D.drops.maps.mansion.push([0.001, "lostearring"]); + D.drops.maps.mansion.push([0.001, "lostearring"]); + D.drops.monsters.croc.push([1, "seashell"]); + D.drops.monsters.croc.push([1, "seashell"]); + D.drops.maps.global = []; + D.monster_gold.bat *= 50; + D.monster_gold.bbpompom *= 75; + D.monster_gold.ghost *= 50; + events.egghunt = 0.1; + } + D.craftmap = {}; + for (var name in G.craft) { + var items = []; + G.craft[name].items.forEach(function (x) { + var name = x[1]; + if (x[2]) { + name += "+" + x[2]; + } + items.push(name); + }); + items.sort(); + var key = items.join(","); + D.craftmap[key] = name; + } + process_game_data(); + for (var name in G.monsters) { + stats.kills[name] = stats.kills[name] || 0; + } + D.base_gold = {}; + for (var mname in G.maps) { + var map = G.maps[mname]; + (map.monsters || []).forEach(function (pack) { + var def = G.monsters[pack.type]; + var drop_value = 0; + var map_value = 0; + var global_value = 0; + var hp_mult = def.hp / 1000.0; + // #BETTER IDEA# + // track damage done + dps utilisation / pack fullness - calculate gold based on that + // /1400.0 /40.000 /14000.0 originally + // 0.0624 is the original modifier + pack.gold = + def.hp * + (0.0782 + + max(def.charge - 40, 0) / 1900.0 + + ((1 - dps_multiplier(def.attack + (def.apiercing || 0) / 2 + (def.rpiercing || 0) / 2)) * 1000) / 120000.0 + + min( + (1 - damage_multiplier(def.armor || 0)) * 1000 + (def.evasion || 0) * 10 + (def.avoidance || 0) * 10, + (1 - damage_multiplier(def.resistance || 0)) * 1000 + + (def.reflection || 0) * 25 + + (def.avoidance || 0) * 10, + ) / + 12000.0) - + def.xp * 0.0172; + if ((map.pvp || map.safe_pvp) && !is_pvp) { + pack.gold *= 2; + } + drop_value = calculate_xvalue(D.drops.monsters[pack.type] || []); + map_value = calculate_xvalue(D.drops.maps[mname] || [], undefined, undefined, hp_mult); + global_value = calculate_xvalue(D.drops.maps.global || [], undefined, undefined, hp_mult); + pack.gold = pack.gold - map_value / 1.002 - drop_value * 1.002; // Originally 1.2 + + if (def.hp <= 2000 && pack.gold < def.hp * 0.0482) { + pack.gold = def.hp * 0.0482; + } else if (pack.gold < def.hp * 0.02808) { + pack.gold = def.hp * 0.02808; + } + + //if(def.hp<2000 && pack.gold0.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, + ); + }); + } + 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 (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; + } +} + +function calculate_xvalue(arr, rec, divide, mult) { + var value = 0; + var total = 0; + // console.log(arr); + 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]]; + 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; + } + 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; + return; + } + 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(); + } + player.p.history.push(event); +} + +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") { + //if(player.type=="mage" || player.type=="priest") return false; + //if(player.type=="rogue") 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; + } + } + 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 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; + } + 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; + } + 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; + } + 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; + } + 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; + } + 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); + } + } + } + player.s[name].ms -= ms; + } + } +} + +function reset_player(player, soft) { + if (!player.p.hardcore) { + player.p = { hardcore: true, dt: {} }; + player.max_stats = { monsters: {} }; + } + 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; + } + } +} + +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); + 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 < S.sold.length; i++) { + if (done) { + return; + } + if (can_stack(S.sold[i], new_item)) { + done = true; + S.sold[i].q = S.sold[i].q + new_item.q; + csold[i] = cache_item(S.sold[i], true); + } + if (S.sold[i].name == new_item.name && S.sold[i].level == new_item.level) { + if (!S.sold[i].p) { + replace = i; + } + count += 1; + } + } + 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; + var count = 0; + var replace = null; + item.rid = randomStr(5); + for (var i = 0; i < S.found.length; i++) { + if (done) { + return; + } + if (can_stack(S.found[i], item)) { + done = true; + S.found[i].q = S.found[i].q + item.q; + cfound[i] = cache_item(S.found[i], true); + } + if (S.found[i].name == item.name && S.found[i].level == item.level) { + if (!S.found[i].p) { + replace = i; + } + count += 1; + } + } + 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; 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; + } + 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); + }); + } + } +} + +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; + } + player.xp += tax * 3.2; +} + +function normalise(data) { + // normalise(data,["gold","gold"],["num","inv"]) + for (var i = 1; i < arguments.length; i++) { + var def = arguments[i]; + var name = def[0]; + var type = def[1]; + var value = data[name]; + if (type == "gold") { + value = max(0, min(parseInt(value) || 0, 99999999999)); + } + data[name] = value; + } +} + +var cloudflare_ips = [ + // https://www.cloudflare.com/ips/ + "103.21.244.0/22", + "103.22.200.0/22", + "103.31.4.0/22", + "104.16.0.0/12", + "108.162.192.0/18", + "131.0.72.0/22", + "141.101.64.0/18", + "162.158.0.0/15", + "172.64.0.0/13", + "173.245.48.0/20", + "188.114.96.0/20", + "190.93.240.0/20", + "197.234.240.0/22", + "198.41.128.0/17", + "2400:cb00::/32", + "2405:b500::/32", + "2606:4700::/32", + "2803:f800::/32", + "2c0f:f248::/32", + "2a06:98c0::/29", ]; -function get_ip_raw(player) -{ - if(!player.socket) player={socket:player}; // so get_ip(socket) works too [06/09/18] - // return player.socket.handshake.address; - // BEWARE: player.socket.request.connection.remoteAddress - try{ - if(player.last_ip) return player.last_ip; - if(player.first_ip) return player.first_ip; - if(player.socket.request['headers']['cf-connecting-ip']) - { - if(range_check.inRange(range_check.displayIP(player.socket.handshake.address),cloudflare_ips)) - { - player.first_ip=player.socket.request['headers']['cf-connecting-ip']; - return player.first_ip; - } - else - { - player.ipx=-1; - } - } - } catch (e){} - try{ return player.socket.request.connection.remoteAddress||player.socket.handshake.address; } catch (e){} - try{ return player.socket.handshake.address; } catch (e){} -} - -function get_ip(player) -{ - var ip=get_ip_raw(player)||""; - return ip.replace("::ffff:",""); -} - -function quick_hash(str) -{ - var hash = 0; - if (str.length == 0) { - return hash; - } - for (var i = 0; i < str.length; i++) { - var char = str.charCodeAt(i); - hash = ((hash<<5)-hash)+char; - hash = hash & hash; - } - return hash; -} - -function verify_steam_ticket(player,ticket) -{ - // Thanks: https://github.com/DoctorMcKay/node-steam-user - try{ - var outer=EncryptedAppTicket.decode(new Buffer(ticket,"hex")); - var decrypted=symmetricDecrypt(outer.encryptedTicket,new Buffer(variables.steam_key,"hex")); - let userData = decrypted.slice(0, outer.cbEncrypteduserdata); - let ownershipTicketLength = decrypted.readUInt32LE(outer.cbEncrypteduserdata); - let ownershipTicket = parseAppTicket(decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength)); - if (ownershipTicket) { - ownershipTicket.userData = userData.toString(); - } - if(ownershipTicket.appID==777150 && ownershipTicket.steamID) - { - player.auth_type="steam"; - player.auth_id=ownershipTicket.steamID; - player.p.steam_id=ownershipTicket.steamID; - delete player.s.authfail; - } - }catch(e) - { - console.log("#A verify_steam_ticket: "+e); - } -} - -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"; - //player.mas_receipt=receipt; - var content={'receipt-data':receipt,password:variables.apple_token}; - var url="https://buy.itunes.apple.com/verifyReceipt"; - request( - { - url:url, - method:"POST", - json:content, - }, - 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) - { - 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); - delete player.s.authfail; - } - else - { - delete player.temp_auth; - 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){ - 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); - 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){ - 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 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; - } - 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++; - } - 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())]; - player.p.entries.push(c); - 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; - 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"); -} - -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 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]); - } - 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 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"}; -} - -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){} -} - -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_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, - }, - gain:0, - players:{}, - }; - 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.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; - } - } - 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; - 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"); - } - } - 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}); - } - 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]; - } - } - } - }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]; - } - - 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); - instance.rage_list.push(map_def); - } - for(var j=0;j=12 is a must."); - }); - 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 resume_instance(instance) -{ - if(!instance.paused) return; - // server_log("Resumed: "+instance.name); - 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"); - restore_state(player); - 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 - } - 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] - }); - }); - 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}; - }); - }); - 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}); - }); - }); - 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=="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=="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; - }); - } - 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; - //server_log("Love Goo: "+JSON.stringify(pack)); - 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; - //server_log("Love Goo: "+JSON.stringify(pack)); - 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}); - }); - }); - 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}); - } - 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; - //server_log("Love Goo: "+JSON.stringify(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}); - }); - }); - 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); - } -} - -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; - - 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); - }); -} - -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; - } - }); - - 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(); - } - } - } - ["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; - 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],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,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); - }); - 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!", - }, - } - E.minutes=340; - if(is_sdk) E.minutes=12; - setInterval(hardcore_loop,60*1000); - } - setInterval(event_loop,1000); - setInterval(tavern_loop,1000); - workers=[ - new_worker(0), - new_worker(1), - new_worker(2), - new_worker(3), - new_worker(4), - new_worker(5), - new_worker(6), - new_worker(7), - ]; -} - -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; - } - } - 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"); - }); -} - -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 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; - } -} - -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); - // 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)); - } - 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 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(); - 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" - request( - { - url:url, - headers:{"Authorization":"Bot "+variables.discord_token}, - method:"POST", - json:{ - "content":message, - } - }, - 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 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={}; - // if(!args.size) args.size=16; - 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.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") - 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; - } - 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}); - } - } - }); - 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 - { - 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}; +function get_ip_raw(player) { + if (!player.socket) { + player = { socket: player }; + } // so get_ip(socket) works too [06/09/18] + // return player.socket.handshake.address; + // BEWARE: player.socket.request.connection.remoteAddress + try { + if (player.last_ip) { + return player.last_ip; + } + if (player.first_ip) { + return player.first_ip; + } + if (player.socket.request["headers"]["cf-connecting-ip"]) { + if (range_check.inRange(range_check.displayIP(player.socket.handshake.address), cloudflare_ips)) { + player.first_ip = player.socket.request["headers"]["cf-connecting-ip"]; + return player.first_ip; + } else { + player.ipx = -1; + } + } + } catch (e) {} + try { + return player.socket.request.connection.remoteAddress || player.socket.handshake.address; + } catch (e) {} + try { + return player.socket.handshake.address; + } catch (e) {} +} + +function get_ip(player) { + var ip = get_ip_raw(player) || ""; + return ip.replace("::ffff:", ""); +} + +function quick_hash(str) { + var hash = 0; + if (str.length == 0) { + return hash; + } + for (var i = 0; i < str.length; i++) { + var char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash = hash & hash; + } + return hash; +} + +function verify_steam_ticket(player, ticket) { + // Thanks: https://github.com/DoctorMcKay/node-steam-user + try { + var outer = EncryptedAppTicket.decode(new Buffer(ticket, "hex")); + var decrypted = symmetricDecrypt(outer.encryptedTicket, new Buffer(variables.steam_key, "hex")); + let userData = decrypted.slice(0, outer.cbEncrypteduserdata); + let ownershipTicketLength = decrypted.readUInt32LE(outer.cbEncrypteduserdata); + let ownershipTicket = parseAppTicket( + decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), + ); + if (ownershipTicket) { + ownershipTicket.userData = userData.toString(); + } + if (ownershipTicket.appID == 777150 && ownershipTicket.steamID) { + player.auth_type = "steam"; + player.auth_id = ownershipTicket.steamID; + player.p.steam_id = ownershipTicket.steamID; + delete player.s.authfail; + } + } catch (e) { + console.log("#A verify_steam_ticket: " + e); + } +} + +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"; + //player.mas_receipt=receipt; + var content = { "receipt-data": receipt, password: variables.apple_token }; + var url = "https://buy.itunes.apple.com/verifyReceipt"; + request( + { + url: url, + method: "POST", + json: content, + }, + 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) { + 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); + delete player.s.authfail; + } else { + delete player.temp_auth; + 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) { + 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); + 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) { + 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 serverhop_logic(player) { + delete player.s.hopsickness; + 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 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; + } + calculate_player_stats(player); + } + var c = [region + server_name, "" + new Date()]; + player.p.entries.push(c); + 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; + } + 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"); + } +} + +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 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 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" }; +} + +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) {} +} + +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_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, + }, + gain: 0, + players: {}, + }; + 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.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; + } + } + 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; + } + 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"); + } + } 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 }); + } + } 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 = {}; + 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]; + } + } + + 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); + instance.rage_list.push(map_def); + } + for (var j = 0; j < map_def.count; j++) { + if (G.monsters[map_def.type].announce) { + setTimeout(new_monster_f(name, map_def), 120 * 1000); + } else { + new_monster(name, map_def); + } + } + } + // for(var i=0;i<(map.specials||[]).length;i++) + // { + // var map_def=map.specials[i]; + // new_monster(name,map_def,1); + // } + // console.log(JSON.stringify(instance.players)); + for (var i = 0; i < (map.npcs || []).length; i++) { + var map_def = map.npcs[i]; + var def = G.npcs[map_def.id]; + if (instance.players[NPC_prefix + def.name]) { + console.log("NPC NAME CLASH: " + def.name); + shutdown_routine(); + } + var npc = (instance.players[NPC_prefix + def.name] = create_npc(def, map_def, instance)); + npcs[map_def.id] = npc; + instance.npcs += 1; + } + 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); + return instance; +} + +function pause_instance(instance) { + server_log("Paused: " + instance.name); + instance.paused = true; +} + +function resume_instance(instance) { + if (!instance.paused) { + return; + } + // server_log("Resumed: "+instance.name); + 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"); + } + restore_state(player); + 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 + } + 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] + }); + }); + 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 }; + } + }); + }); + 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 }); + } + }); + }); + 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 == "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 == "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; + } + } + 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 }); + } + 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 }); + } + 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; + //server_log("Love Goo: "+JSON.stringify(pack)); + 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 }); + } + }); + }); + 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); + // 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 }); + } + }); + }); + 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); + } +} + +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 == "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(); + 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.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; + } + }); + + 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; + var 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(); + } + } + } + ["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; + } + 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" }); + } + } + } + } + }); + 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); + }); + 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!", + }, + }; + E.minutes = 340; + if (is_sdk) { + E.minutes = 12; + } + setInterval(hardcore_loop, 60 * 1000); + } + setInterval(event_loop, 1000); + setInterval(tavern_loop, 1000); + workers = [ + new_worker(0), + new_worker(1), + new_worker(2), + new_worker(3), + new_worker(4), + new_worker(5), + new_worker(6), + new_worker(7), + ]; +} + +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 < S.sold.length; i++) { + csold[i] = cache_item(S.sold[i], true); + } + for (var i = 0; i < S.found.length; i++) { + cfound[i] = cache_item(S.found[i], true); + } + if (!S.ugrace) { + S.ugrace = []; + S.cgrace = []; + for (var i = 0; i < 25; i++) { + S.ugrace[i] = 24; + S.cgrace[i] = 24; + } + } +} + +function get_monsters(type) { + var l = []; + for (var name in instances) { + for (var id in instances[name].monsters) { + var monster = instances[name].monsters[id]; + if (monster.type == type) { + l.push(monster); + } + } + } + return l; +} + +function get_monster(type) { + return get_monsters(type)[0]; +} + +function add_item_property(item, prop) { + if (prop == "legacy") { + return; + } + if (!item.ps) { + item.ps = []; + } + if (item.p && !in_arr(item.p, item.ps)) { + item.ps.push(item.p); + } // backwards compatibility + if (!in_arr(prop, item.ps)) { + item.ps.push(prop); + } + item.p = prop; +} + +function add_condition(target, condition, args) { + var def = G.conditions[condition]; + if (!def) { + return; + } + if (!args) { + args = {}; + } + var response = { response: "condition", name: condition, cevent: true }; + var duration = args.duration || args.ms || def.duration; + if (duration == undefined) { + duration = 1000; + } + var C = { ms: duration }; + if (condition == "poisoned" && target.pnresistance) { + if (Math.random() < (target.pnresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "poison_resist", id: target.id }); + } + + duration *= (100 - target.pnresistance) / 100.0; + } + if (condition == "stunned") { + if (Math.random() < (target.phresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "stun_resist", id: target.id }); + } + + target.abs = true; + target.moving = false; + } + if (condition == "burned") { + if (Math.random() < (target.firesistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "fire_resist", id: target.id }); + } + + duration = 5000; + if (!args.attack) { + args.attack = 1000; + } + if (args.divider == 3 && target.s.burned && target.s.burned.ms) { + duration = min( + 12000, + max(duration + 400, min(8000, parseInt(duration / 4 + (50 * args.attack) / (target.s.burned.intensity + 200)))), + ); + } + C.intensity = max( + (target.s.burned && target.s.burned.intensity) || 1, + parseInt(((target.s.burned && target.s.burned.intensity) || 0) / (args.divider || 3) + args.attack), + ); + C.fid = args.fid; + disappearing_text({}, target, "BURN!", { xy: 1, size: "huge", color: "burn", nv: 1 }); //target.is_player&&"huge"||undefined + } + if (condition == "deepfreezed") { + if (Math.random() < (target.fzresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "freeze_resist", id: target.id }); + } + } + if (condition == "woven") { + C.s = min((target.is_monster && 20) || 5, (target.s.woven && target.s.woven.s + 1) || 1); + C.speed = -3 * condition.s; + } + duration = max((target.s[condition] && target.s[condition].ms) || 0, duration); + if (target.stresistance && def && def.debuff) { + duration *= (100 - target.stresistance) / 100.0; + } + target.s[condition] = C; + C.ms = response.duration = duration; + server_log(C); + if (args.from) { + C.f = args.from.name; + } + if (args.f) { + C.f = args.f; + } + if (C.f) { + response.from = C.f; + } + target.cid++; + target.u = true; + if (target.socket) { + target.hitchhikers.push(["game_response", response]); + } + return true; +} + +function consume_mp(player, mp, target) { + var mult = 1; + if (target && target.humanoid) { + mult = 5; + } + if (player.a.restore_mp && Math.random() < (player.a.restore_mp.attr0 * mult) / 100.0) { + player.mp += mp * 2; + xy_emit(player, "ui", { id: player.id, type: "restore_mp", amount: mp * 2 }); + } else { + player.mp -= (mp * (100 - (player.mp_reduction || 0))) / 100.0; + } + player.mp = parseInt(max(0, player.mp)); + player.mp = min(player.mp, player.max_mp); +} + +function game_response(response, data) { + if (!data) { + data = {}; + } + data.response = response; + current_socket.emit("game_response", data); +} + +function fail_response(response, place, data) { + if (data && is_string(data)) { + data = { reason: data }; + } + if (!response) { + response = "data"; + } + if (is_object(response)) { + data = response; + response = "data"; + place = ls_method; + } + if (is_object(place)) { + data = place; + place = ls_method; + } + if (!data) { + data = {}; + } + if (!place) { + place = ls_method; + } + data.response = response; + data.place = place; + data.failed = true; + current_socket.emit("game_response", data); +} + +function success_response(response, place, data) { + if (!response) { + response = "data"; + } + if (is_object(response)) { + data = response; + response = "data"; + place = ls_method; + } + if (place && is_object(place)) { + data = place; + place = ls_method; + } + if (!data) { + data = {}; + } + if (!place) { + place = ls_method; + } + if (data.success !== false) { + data.success = true; + } + data.response = response; + data.place = place; + current_socket.emit("game_response", data); +} + +function consume_skill(player, name, reuse) { + var penalty_cd = (player.s.penalty_cd && player.s.penalty_cd.ms) || 0; + var multiplier = 1; + //if(name=="attack" || G.skills[name].share=="attack") + if (G.skills[name].share) { + multiplier = G.skills[name].cooldown_multiplier || 1; + name = G.skills[name].share; + } + var cooldown = G.skills[name].cooldown; + if (reuse) { + cooldown = G.skills[name].reuse_cooldown; + } + if (!cooldown) { + return; + } + player.last[name] = future_ms(min(penalty_cd, 10000) + cooldown * (multiplier - 1)); + player.socket.emit("skill_timeout", { + name: name, + ms: min(penalty_cd, 10000) + cooldown * multiplier, + penalty: min(penalty_cd, 10000), + }); + // player.socket.emit("eval",{code:"skill_timeout('"+name+"',"+(min(penalty_cd,10000)+cooldown*multiplier)+")"}); +} + +function get_entity(name) { + if (players[name_to_id[name]]) { + return players[name_to_id[name]]; + } + var l = []; + for (var iname in instances) { + if (instances[iname].monsters[name]) { + return instances[iname].monsters[name]; + } + } + return null; +} + +function get_player(name) { + return players[name_to_id[name]]; +} + +function realm_broadcast(event, data) { + data.sname = region + " " + server_name; + appengine_call("broadcast", { event: event, data: JSON.stringify(data) }); + // broadcast(event,data); +} + +function broadcast(event, data) { + io.emit(event, data); + if (event == "notice" || (event == "server_message" && gameplay == "normal")) { + var to_log = false; + if (data.sname && data.sname != region + " " + server_name) { + data.message += " [" + data.sname + "]"; + } + if (event == "notice") { + data.color = "orange"; + } + if ( + event == "server_message" && + !data.event && + !data.nod && + (!data.sname || data.sname == region + " " + server_name) + ) { + var message = data.message; + if (data.sname) { + message += " [" + region + " " + server_name + "]"; + } + if (data.discord == "orange") { + message = "```css\n[" + message + "```"; + } // no ] + if (data.discord == "red") { + message = "```diff\n-" + message + "```"; + } + discord_call(message); + to_log = true; + } + if (to_log) { + appengine_call( + "server_event", + { + event: event, + keyword: variables.keyword, + id: server_id, + message: data.message, + color: data.color || "", + }, + function (result) { + server_log("Server notice: " + data.message); + }, + ); + } + } +} + +function instance_emit(name, event, data) { + var instance = instances[name] || name; + if (gameplay == "hardcore" && event != "tavern" && event != "dice") { + return broadcast(event, data); + } + for (var id in instance.players) { + var player = instance.players[id]; + if (!player.npc) { + player.socket.emit(event, data); + } + } +} + +function party_emit(name, event, data, args) { + if (!parties[name]) { + return; + } + // console.log(parties[name]); + var owners = []; + var owner = get_player(data.owner); + parties[name].forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (args && args.instance && player.in != args.instance) { + return; + } + if (0 && in_arr(event, ["disappearing_text"])) { + player.socket.emit(event, data); + } //volatile. + else { + player.socket.emit(event, data); + } + if (event == "partym") { + owners[player.owner] = owners[player.owner] || []; + owners[player.owner].push(player.name); + } + }); + if (event == "partym" && owner) { + appengine_call("log_chat", { + to: Object.entries(owners), + type: "party", + message: data.message, + fro: owner.name, + author: owner.owner, + }); + } +} + +function leave_party(name, leaver) { + // [10/07/18]: For a long time chased oddities around party routines that didn't make sense, yesterday I realised that .emit causes a "disconnect" to be handled right inside this function, rather than afterwards, causing the oddities and irregularities - so - after a .emit, there's no guarantee that the player will still be there + var newparty = []; + var oldparty = parties[name]; + if (!oldparty) { + leaver.party = null; + console.log("#X NO PARTY " + name); + return; + } // Don't know the cause, maybe a disconnect triggering on the accept routines? [12/07/18] + parties[name].forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (!player) { + console.log("#X SHOULD'VE FOUND " + player_name); + return; + } + player.party = null; + if (leaver.name != player.name) { + newparty.push(player_name); + } + }); + delete parties[name]; + if (newparty.length >= 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; + } + } + } 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"); + }); +} + +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 }; + 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; + var y = entity.y; + var 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] < x && + x < player.x + player.vision[0] && + player.y - player.vision[1] < y && + y < player.y + player.vision[1]) || + player.id == must + ) { + if (event == "light" && (player.type == "rogue" || player.s.invis) && distance(player, entity) < 300) { + player.last.invis = new Date(); + delete player.s.invis; + player.socket.emit("light", { name: data.name, affected: 1 }); + resend(player, "u"); + } else if ((event == "upgrade" || event == "ui") && entity.name != player.name) { + player.socket.emit(event, data); + } //volatile. [02/02/18] + else { + player.socket.emit(event, data); + if (event == "chat_log" && data.p) { + owners[player.owner] = owners[player.owner] || []; + owners[player.owner].push(player.name); + } + } + // else if(!data.nv && in_arr(event,["disappearing_text","upgrade"])) player.socket.emit(event,data); //volatile. + } + } + for (var id in instances[entity.in].observers) { + var player = instances[entity.in].observers[id]; + if ( + player.x - player.vision[0] < x && + x < player.x + player.vision[0] && + player.y - player.vision[1] < y && + y < player.y + player.vision[1] + ) { + if (event == "upgrade") { + player.socket.emit(event, data); + } //volatile. [02/02/18] + else if (!data.nv && in_arr(event, ["disappearing_text", "upgrade"])) { + player.socket.emit(event, data); + } //volatile. + else { + player.socket.emit(event, data); + } + } + } + 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_u_logic(element) { + // sets .u so updates are sent - this element might enter someone's vision + var u = false; + + 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 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); + // 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)); + } + } 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 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(); + } + 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"; + } + request( + { + url: url, + headers: { Authorization: "Bot " + variables.discord_token }, + method: "POST", + json: { + content: message, + }, + }, + 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 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; + 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) { + 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. +} + +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; + 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]); + 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"); + } +} + +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, +}; +var item_trade_p_ignore = { grace: true, o: true, oo: true, src: true }; // DOC // .name -> key from G.items @@ -2652,1226 +3557,1776 @@ 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; - } - 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]; - 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); - return slots; - } - 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 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]); - }); - for(var i=0;i42) 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; - } - 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.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.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])}; - } - - 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 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) - player.xyh.push(current); - 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; - return name; -} - -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 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; - }); - return safe; -} - -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; - }); - return safe; -} - -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); - // [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}; - }); - 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)= 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"]; + } + 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 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]); + }); + for (var i = 0; i < player.items.length; i++) { + player.citems[i] = cache_item(player.items[i]); + } + reslot_player(player); +} + +function init_bank(player) { + player.cuser = {}; + for (var pack in bank_packs) { + if (!player.user[pack]) { + continue; + } + player.cuser[pack] = []; + for (var i = 0; i < player.user[pack].length; i++) { + if (!player.user[pack][i]) { + player.cuser[pack][i] = null; + continue; + } + if (player.user[pack][i].expires) { + player.user[pack][i].expires = new Date(player.user[pack][i].expires); + } + player.cuser[pack][i] = cache_item(player.user[pack][i]); + } + } +} + +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; + } + 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; i < player.items.length; i++) { + if (!player.items[i]) { + continue; + } + delete player.items[i].m; + 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; + } + } + 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.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]) }; + } + } + + 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 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(); + } +} + +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; + } + 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 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; + } + }); + return safe; +} + +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; + } + }); + return safe; +} + +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); + // [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 }; + } + }); + 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) < abs(x - x2)) { + return x1; + } + return x2; +} + +function phash(x, y) { + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + return x + "|" + y; } // Without rphash, halloween -448.0000001,-158.0000001 rounds to a corner which is a 2 -function rphash(x,y) -{ - if(x.x!==undefined) y=x.y,x=x.x; - x=smap_round(x); y=smap_round(y); - return x+"|"+y; +function rphash(x, y) { + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = smap_round(x); + y = smap_round(y); + return x + "|" + y; } -var smap_data={},smap_step=10,smap_edge=60; // for smap_step 24, the edge was 40 - also check out access_visualize_smap.js for a visualization +var smap_data = {}; +var smap_step = 10; +var smap_edge = 60; // for smap_step 24, the edge was 40 - also check out access_visualize_smap.js for a visualization // if(is_sdk) smap_step=24; // 10 takes toooo long [22/06/18] -var hiding_places=[]; -function server_bfs(map) -{ - if(precomputed && precomputed.version==G.version || precomputed && precomputed.smap_data) { smap_data[map]=precomputed.smap_data[map]; amap_data[map]=precomputed.amap_data[map]; return; } - if(G.maps[map].no_bounds){ smap_data[map]=-1; amap_data[map]={}; return; } - if(is_sdk && variables.fast_sdk && !(map=="level1" || map=="arena")) { smap_data[map]=-1; amap_data[map]={}; return; } - server_bfs2(map); - smap_data[map]={}; - var queue=[],visited={},start=0,level=0,chiding_places=[]; - function push(x,y,c) - { - x=parseInt(x)-(parseInt(x)%smap_step); - y=parseInt(y)-(parseInt(y)%smap_step); - var hash=phash(x,y); - if(visited[hash] && visited[hash].l<=c) return; - if(!c && !G.maps[map].pvp && !G.maps[map].instance && Math.random()<0.01) chiding_places.push([map,x,y]); - if(c==level) queue.push([x,y]); - visited[hash]={l:c,x:x,y:y}; - smap_data[map][hash]=c; - } - for(level=0;level<7;level++) - { - if(level==0) - { - G.maps[map].spawns.forEach(function(s){ - var x=s[0],y=s[1],done=false; - x=parseInt(x)-(parseInt(x)%smap_step); - y=parseInt(y)-(parseInt(y)%smap_step); - var current=[x,y]; - [[0,0],[0,smap_step],[0,-smap_step],[smap_step,0],[-smap_step,0]].forEach(function(m){ - if(!done && can_move({map:map,x:x,y:y,going_x:current[0]+m[0],going_y:current[1]+m[1]})) - push(current[0]+m[0],current[1]+m[1],level),done=true; - }); - }) - } - else - { - for(var h in visited) - { - var e=visited[h]; - if(e.l==level) - { - queue.push([e.x,e.y]); - } - } - } - while(start50000) - { - 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); - } - }); - } - 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); - 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; - }); - 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; - }); - 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); - result.push("bad"); - return result; - } - return null; - } - var current=heap.removeTop(),dist=point_distance(current.x,current.y,tx,ty),rnd=Math.random()*100; - // console.log([current.x,current.y,visited[current.x+"|"+current.y]]); - 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); - 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); - } - if(dist+current.bad-rnd4000) 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(); - } -} - -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 +var hiding_places = []; +function server_bfs(map) { + if ((precomputed && precomputed.version == G.version) || (precomputed && precomputed.smap_data)) { + smap_data[map] = precomputed.smap_data[map]; + amap_data[map] = precomputed.amap_data[map]; + return; + } + if (G.maps[map].no_bounds) { + smap_data[map] = -1; + amap_data[map] = {}; + return; + } + if (is_sdk && variables.fast_sdk && !(map == "level1" || map == "arena")) { + smap_data[map] = -1; + amap_data[map] = {}; + return; + } + server_bfs2(map); + smap_data[map] = {}; + var queue = []; + var visited = {}; + var start = 0; + var level = 0; + var chiding_places = []; + function push(x, y, c) { + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + var hash = phash(x, y); + if (visited[hash] && visited[hash].l <= c) { + return; + } + if (!c && !G.maps[map].pvp && !G.maps[map].instance && Math.random() < 0.01) { + chiding_places.push([map, x, y]); + } + if (c == level) { + queue.push([x, y]); + } + visited[hash] = { l: c, x: x, y: y }; + smap_data[map][hash] = c; + } + for (level = 0; level < 7; level++) { + if (level == 0) { + G.maps[map].spawns.forEach(function (s) { + var x = s[0]; + var y = s[1]; + var done = false; + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + var current = [x, y]; + [ + [0, 0], + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + if (!done && can_move({ map: map, x: x, y: y, going_x: current[0] + m[0], going_y: current[1] + m[1] })) { + push(current[0] + m[0], current[1] + m[1], level); + done = true; + } + }); + }); + } else { + for (var h in visited) { + var e = visited[h]; + if (e.l == level) { + queue.push([e.x, e.y]); + } + } + } + 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); + } + }); + } + 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]; j < current[2]; j += smap_step) { + push(current[0], j, 0); + [ + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + push(current[0] + m[0], j + m[1], 1); + }); + } + } + for (var i = 0; i < (G.maps[map].data.y_lines || []).length; i++) { + var current = G.maps[map].data.y_lines[i]; + push(current[1], current[0], 0); + push(current[2], current[0], 0); + for (var j = current[1]; j < current[2]; j += smap_step) { + push(j, current[0], 0); + [ + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + push(j + m[0], current[0] + m[1], 1); + }); + } + } + } + start = 0; + queue = []; + } + if (chiding_places.length) { + Array.prototype.push.apply(hiding_places, chiding_places); + } +} + +function amap_round(x) { + // for blink, port, magiport + var x1 = parseInt(x) - (parseInt(x) % amap_step); + var x2 = x1 + amap_step; + if (x < 0) { + x2 = x1 - amap_step; + } + if (abs(x - x1) < abs(x - x2)) { + return x1; + } + return x2; +} + +function phash2(x, y) { + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + 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; + } + 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; + } + }); + 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; + } + }); + 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 = (tx < sx && -1) || 1; + var step = 8; + for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { + if (!amap_data[map][amap_round(sx + p * s * step) + "|" + amap_round(ty)]) { + return false; + } + } + } else if (sx == tx) { + var p = (ty < sy && -1) || 1; + var step = 8; + for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { + if (!amap_data[map][amap_round(tx) + "|" + amap_round(sy + p * s * step)]) { + return false; + } + } + } else { + var len = point_distance(sx, sy, tx, ty); + var p = (tx < sx && -1) || 1; + var step = 5; + for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { + var x = sx + p * s * step; + var y = sy + ((ty - sy) * abs(s * step)) / abs(tx - sx); + //console.log("x-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); + if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { + return false; + } + } + var p = (ty < sy && -1) || 1; + var step = 5; + for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { + var x = sx + ((tx - sx) * abs(s * step)) / abs(ty - sy); + var y = sy + p * s * step; + //console.log("y-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); + if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { + return false; + } + } + } + return true; +} + +function random_place(map) { + var place = random_one(Object.keys(amap_data[map] || {})); + if (!place) { + return null; + } + var xy = place.split("|"); + return { x: parseInt(xy[0]), y: parseInt(xy[1]), map: map, in: map }; +} + +function fast_astar(args) { + var map = args.map; + var sx = args.sx; + var sy = args.sy; + var tx = args.tx; + var ty = args.ty; + var heap = vHeap(); + var visited = {}; + var total = 0; + var start = new Date(); + var best = 999999999999; + var theone = null; + var good = false; + function hpush(cx, cy, fr, dir, bad) { + // if(visited[cx+"|"+cy]) return; + var value = point_distance(cx, cy, tx, ty); + var hash = cx + "|" + cy; + if (amap_data[map][hash] < 4) { + bad += 2; + } else if (amap_data[map][hash] < 6) { + bad += 1; + } else if (amap_data[map][hash] < 8) { + bad += 0.5; + } + total += 1; + // console.log(["hpush",total,cx,cy,bad,value]); + heap.insert({ value: value + bad * 0.25, x: cx, y: cy, bad: bad, dir: dir }); + visited[hash] = fr; + } + function finalise(current) { + var dx = current.x; + var dy = current.y; + path = [[dx, dy]]; + while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { + dx = visited[dx + "|" + dy].split("|"); + dy = parseInt(dx[1]); + dx = parseInt(dx[0]); + path.push([dx, dy]); + } + path.reverse(); + // path.push([tx,ty]); // Looks bad + dx = path[0][0]; + dy = path[0][1]; + // console.log(path); + for ( + var i = 1; + i < path.length; + i++ // 1ms at max [17/08/20] + ) { + if (can_amove(map, sx, sy, path[i][0], path[i][1])) { + dx = path[i][0]; + dy = path[i][1]; + } else { + break; + } + } + // if(!good && point_distance(sx,sy,dx,dy)<30) return null; + server_log([total, dx, dy, mssince(start)]); + return [dx, dy]; + } + for (var step = 1; step <= 2; step++) { + if (heap.array.length) { + break; + } + [ + [0, 0, 0, 0], + [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 = amap_round(sx + m[0] * step); + var cy = amap_round(sy + m[1] * step); + if (amap_data[map][cx + "|" + cy]) { + hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); + } + }); + } + 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(); + 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; + 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_astar"]); +} + +function fast_abfs(monster, tx, ty) { + var map = monster.map; + var sx = monster.x; + var sy = monster.y; + var start = 0; + var last = 0; + var queue = []; + var visited = {}; + var start_t = new Date(); + var best = 999999999999; + var theone = null; + var good = false; + function hpush(cx, cy, fr, dir, bad) { + var value = point_distance(cx, cy, tx, ty); + var hash = cx + "|" + cy; + queue[last++] = { x: cx, y: cy, bad: bad, dir: dir }; + visited[hash] = fr; + } + function finalise(current) { + var dx = current.x; + var dy = current.y; + path = [[dx, dy]]; + while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { + dx = visited[dx + "|" + dy].split("|"); + dy = parseInt(dx[1]); + dx = parseInt(dx[0]); + path.push([dx, dy]); + } + path.reverse(); + // path.push([tx,ty]); // Looks bad + dx = path[0][0]; + dy = path[0][1]; + for ( + var i = 1; + i < path.length; + i++ // 1ms at max [17/08/20] + ) { + if (can_amove(map, sx, sy, path[i][0], path[i][1])) { + dx = path[i][0]; + dy = path[i][1]; + } else { + break; + } + } + if (!good && point_distance(sx, sy, dx, dy) < 30) { + return null; + } + server_log([last, dx, dy, mssince(start_t)]); + return [dx, dy]; + } + for (var step = 1; step <= 2; step++) { + if (last) { + break; + } + [ + [sx, sy, 0, 0], + [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 = amap_round(sx + m[0] * step); + var cy = amap_round(sy + m[1] * step); + if (amap_data[map][cx + "|" + cy]) { + hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); + } + }); + } + while (start < last) { + if (last - start > 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; + } + + 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(); + } + } +} + +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 symmetricDecrypt(input, key, checkHmac) { - 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); - aesData.end(input.slice(16)); - var plaintext = aesData.read(); - - if (checkHmac) { - // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC - var remotePartialHmac = iv.slice(0, iv.length - 3); - var random = iv.slice(iv.length - 3, iv.length); - var hmac = crypto.createHmac("sha1", key.slice(0, 16)); - hmac.update(random); - hmac.update(plaintext); - if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { - throw new Error("Received invalid HMAC from remote host."); - } - } - - return plaintext; -}; + 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); + aesData.end(input.slice(16)); + var plaintext = aesData.read(); + + if (checkHmac) { + // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC + var remotePartialHmac = iv.slice(0, iv.length - 3); + var random = iv.slice(iv.length - 3, iv.length); + var hmac = crypto.createHmac("sha1", key.slice(0, 16)); + hmac.update(random); + hmac.update(plaintext); + if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { + throw new Error("Received invalid HMAC from remote host."); + } + } + + return plaintext; +} function parseAppTicket(ticket) { - // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl - - // console.log(ticket); - if (!ByteBuffer.isByteBuffer(ticket)) { - ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); - } - - let details = {}; - - try { - let initialLength = ticket.readUint32(); - // console.log(initialLength); - if (initialLength == 20) { - // This is a full appticket, with a GC token and session header (in addition to ownership ticket) - details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation - - details.gcToken = ticket.readUint64().toString(); - //details.steamID = new SteamID(ticket.readUint64().toString()); - ticket.skip(8); // the SteamID gets read later on - details.tokenGenerated = new Date(ticket.readUint32() * 1000); - - if (ticket.readUint32() != 24) { - // SESSIONHEADER should be 24 bytes. - return null; - } - - ticket.skip(8); // unknown 1 and unknown 2 - details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); - ticket.skip(4); // filler - details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms - details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to - - if (ticket.readUint32() + ticket.offset != ticket.limit) { - // OWNERSHIPSECTIONWITHSIGNATURE sectlength - return null; - } - } else { - ticket.skip(-4); - } - - // 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) { - return null; - } - - let i, j, dlc; - - details.version = ticket.readUint32(); - details.steamID = ticket.readUint64().toString(); - details.appID = ticket.readUint32(); - details.ownershipTicketExternalIP = ticket.readUint32(); - details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( - details.ownershipFlags = ticket.readUint32(); - details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); - details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); - details.licenses = []; - // return details; - - let licenseCount = ticket.readUint16(); - for (i = 0; i < licenseCount; i++) { - details.licenses.push(ticket.readUint32()); - } - - details.dlc = []; - - let dlcCount = ticket.readUint16(); - for (i = 0; i < dlcCount; i++) { - dlc = {}; - dlc.appID = ticket.readUint32(); - dlc.licenses = []; - - licenseCount = ticket.readUint16(); - - for (j = 0; j < licenseCount; j++) { - dlc.licenses.push(ticket.readUint32()); - } - - details.dlc.push(dlc); - } - - ticket.readUint16(); // reserved - if (ticket.offset + 128 == ticket.limit) { - // Has signature - details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); - } - - let date = new Date(); - details.isExpired = details.ownershipTicketExpires < date; - 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); - return details; - return null; // not a valid ticket - } - - return details; + // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl + + // console.log(ticket); + if (!ByteBuffer.isByteBuffer(ticket)) { + ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); + } + + let details = {}; + + try { + let initialLength = ticket.readUint32(); + // console.log(initialLength); + if (initialLength == 20) { + // This is a full appticket, with a GC token and session header (in addition to ownership ticket) + details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation + + details.gcToken = ticket.readUint64().toString(); + //details.steamID = new SteamID(ticket.readUint64().toString()); + ticket.skip(8); // the SteamID gets read later on + details.tokenGenerated = new Date(ticket.readUint32() * 1000); + + if (ticket.readUint32() != 24) { + // SESSIONHEADER should be 24 bytes. + return null; + } + + ticket.skip(8); // unknown 1 and unknown 2 + details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); + ticket.skip(4); // filler + details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms + details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to + + if (ticket.readUint32() + ticket.offset != ticket.limit) { + // OWNERSHIPSECTIONWITHSIGNATURE sectlength + return null; + } + } else { + ticket.skip(-4); + } + + // 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 + ) { + return null; + } + + let i; + let j; + let dlc; + + details.version = ticket.readUint32(); + details.steamID = ticket.readUint64().toString(); + details.appID = ticket.readUint32(); + details.ownershipTicketExternalIP = ticket.readUint32(); + details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( + details.ownershipFlags = ticket.readUint32(); + details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); + details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); + details.licenses = []; + // return details; + + let licenseCount = ticket.readUint16(); + for (i = 0; i < licenseCount; i++) { + details.licenses.push(ticket.readUint32()); + } + + details.dlc = []; + + let dlcCount = ticket.readUint16(); + for (i = 0; i < dlcCount; i++) { + dlc = {}; + dlc.appID = ticket.readUint32(); + dlc.licenses = []; + + licenseCount = ticket.readUint16(); + + for (j = 0; j < licenseCount; j++) { + dlc.licenses.push(ticket.readUint32()); + } + + details.dlc.push(dlc); + } + + ticket.readUint16(); // reserved + if (ticket.offset + 128 == ticket.limit) { + // Has signature + details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); + } + + let date = new Date(); + details.isExpired = details.ownershipTicketExpires < date; + 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); + 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"); + +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] + + // the ipAddress we return + var ipAddress; + + // 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"]; + + // 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"]; + + // 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 reqInfoRemoteAddress = req.info ? req.info.remoteAddress : null; + + // x-client-ip + if (clientIp) { + ipAddress = clientIp; + } + + // 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 + // 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(","); + ipAddress = forwardedIps[0]; + } + + // 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 + // (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 + else if (clusterClientIp) { + ipAddress = clusterClientIp; + } + + // x-forwarded + else if (forwardedAlt) { + ipAddress = forwardedAlt; + } + + // forwarded-for + else if (forwardedFor) { + ipAddress = forwardedFor; + } + + // forwarded + else if (forwarded) { + ipAddress = forwarded; + } + + // remote address checks + else if (reqConnectionRemoteAddress) { + ipAddress = reqConnectionRemoteAddress; + } 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; +} -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 - // 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] - - // the ipAddress we return - var ipAddress; - - // 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']; - - // 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']; - - // 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 reqInfoRemoteAddress = req.info ? req.info.remoteAddress : null; - - // x-client-ip - if (clientIp) { - ipAddress = clientIp; - } - - // 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 - // 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(','); - ipAddress = forwardedIps[0]; - } - - // 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 - // (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 - else if (clusterClientIp) { - ipAddress = clusterClientIp; - } - - // x-forwarded - else if (forwardedAlt) { - ipAddress = forwardedAlt; - } - - // forwarded-for - else if (forwardedFor) { - ipAddress = forwardedFor; - } - - // forwarded - else if (forwarded) { - ipAddress = forwarded; - } - - // remote address checks - else if (reqConnectionRemoteAddress) { - ipAddress = reqConnectionRemoteAddress; - } - 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"; - 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 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 +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; + } + } + } + } + } + } + } + } + } + } + } +} + +/* 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 +} +/* 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; - send_hardcore_rewards(); - shutdown_routine(); - } - broadcast("hardcore_info",{E:E}); +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 }); } // #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}], - ]; - 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) - 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})}); - } - } -} - -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}); - } - } - else - { - - } -}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_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);}} \ No newline at end of file +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, + }); + } + }); + 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 }), + }); + } + } +} + +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; + var old = item.acc; + var rr = G.achievements[ach].rr || 1; + item.ach = ach; + item.acc += amount || 1; + if (item.acc < needed) { + if (parseInt(old / rr) != parseInt(item.acc / rr)) { + player.socket.emit("achievement_progress", { name: ach, count: item.acc, needed: needed }); + } + } else { + add_item_property(item, G.achievements[ach].title); + delete item.ach; + delete item.acc; + add_achievement(player, ach); + cache_player_items(player); + resend(player, "u+cid+reopen"); + } +} + +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); + } +} + +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); + } +} + +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_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 }); + } + } else { + } + } 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_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 0e98fff8..302dc8ba 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{ - //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){ - console.log(e); - } +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) { + 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 d2f59103..4bdad915 100644 --- a/node/test.js +++ b/node/test.js @@ -1,150 +1,176 @@ -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, ''); - aesIv.setAutoPadding(false); - aesIv.end(input.slice(0, 16)); - var iv = aesIv.read(); - - var aesData = crypto.createDecipheriv('aes-256-cbc', key, iv); - aesData.end(input.slice(16)); - var plaintext = aesData.read(); - - if (checkHmac) { - // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC - var remotePartialHmac = iv.slice(0, iv.length - 3); - var random = iv.slice(iv.length - 3, iv.length); - var hmac = crypto.createHmac("sha1", key.slice(0, 16)); - hmac.update(random); - hmac.update(plaintext); - if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { - throw new Error("Received invalid HMAC from remote host."); - } - } - - return plaintext; -}; + 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); + aesData.end(input.slice(16)); + var plaintext = aesData.read(); + + if (checkHmac) { + // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC + var remotePartialHmac = iv.slice(0, iv.length - 3); + var random = iv.slice(iv.length - 3, iv.length); + var hmac = crypto.createHmac("sha1", key.slice(0, 16)); + hmac.update(random); + hmac.update(plaintext); + if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { + throw new Error("Received invalid HMAC from remote host."); + } + } + + return plaintext; +} function parseAppTicket(ticket) { - // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl - - console.log(ticket); - if (!ByteBuffer.isByteBuffer(ticket)) { - ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); - } - - let details = {}; - - try { - let initialLength = ticket.readUint32(); - console.log(initialLength); - if (initialLength == 20) { - // This is a full appticket, with a GC token and session header (in addition to ownership ticket) - details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation - - details.gcToken = ticket.readUint64().toString(); - //details.steamID = new SteamID(ticket.readUint64().toString()); - ticket.skip(8); // the SteamID gets read later on - details.tokenGenerated = new Date(ticket.readUint32() * 1000); - - if (ticket.readUint32() != 24) { - // SESSIONHEADER should be 24 bytes. - return null; - } - - ticket.skip(8); // unknown 1 and unknown 2 - details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); - ticket.skip(4); // filler - details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms - details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to - - if (ticket.readUint32() + ticket.offset != ticket.limit) { - // OWNERSHIPSECTIONWITHSIGNATURE sectlength - return null; - } - } else { - ticket.skip(-4); - } - - // 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) { - return null; - } - - let i, j, dlc; - - details.version = ticket.readUint32(); - details.steamID = ticket.readUint64().toString(); - details.appID = ticket.readUint32(); - details.ownershipTicketExternalIP = ticket.readUint32(); - details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( - details.ownershipFlags = ticket.readUint32(); - details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); - details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); - details.licenses = []; - // return details; - - let licenseCount = ticket.readUint16(); - for (i = 0; i < licenseCount; i++) { - details.licenses.push(ticket.readUint32()); - } - - details.dlc = []; - - let dlcCount = ticket.readUint16(); - for (i = 0; i < dlcCount; i++) { - dlc = {}; - dlc.appID = ticket.readUint32(); - dlc.licenses = []; - - licenseCount = ticket.readUint16(); - - for (j = 0; j < licenseCount; j++) { - dlc.licenses.push(ticket.readUint32()); - } - - details.dlc.push(dlc); - } - - ticket.readUint16(); // reserved - if (ticket.offset + 128 == ticket.limit) { - // Has signature - details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); - } - - let date = new Date(); - details.isExpired = details.ownershipTicketExpires < date; - 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); - 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"); - + // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl + + console.log(ticket); + if (!ByteBuffer.isByteBuffer(ticket)) { + ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); + } + + let details = {}; + + try { + let initialLength = ticket.readUint32(); + console.log(initialLength); + if (initialLength == 20) { + // This is a full appticket, with a GC token and session header (in addition to ownership ticket) + details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation + + details.gcToken = ticket.readUint64().toString(); + //details.steamID = new SteamID(ticket.readUint64().toString()); + ticket.skip(8); // the SteamID gets read later on + details.tokenGenerated = new Date(ticket.readUint32() * 1000); + + if (ticket.readUint32() != 24) { + // SESSIONHEADER should be 24 bytes. + return null; + } + + ticket.skip(8); // unknown 1 and unknown 2 + details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); + ticket.skip(4); // filler + details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms + details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to + + if (ticket.readUint32() + ticket.offset != ticket.limit) { + // OWNERSHIPSECTIONWITHSIGNATURE sectlength + return null; + } + } else { + ticket.skip(-4); + } + + // 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 + ) { + return null; + } + + let i; + let j; + let dlc; + + details.version = ticket.readUint32(); + details.steamID = ticket.readUint64().toString(); + details.appID = ticket.readUint32(); + details.ownershipTicketExternalIP = ticket.readUint32(); + details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( + details.ownershipFlags = ticket.readUint32(); + details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); + details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); + details.licenses = []; + // return details; + + let licenseCount = ticket.readUint16(); + for (i = 0; i < licenseCount; i++) { + details.licenses.push(ticket.readUint32()); + } + + details.dlc = []; + + let dlcCount = ticket.readUint16(); + for (i = 0; i < dlcCount; i++) { + dlc = {}; + dlc.appID = ticket.readUint32(); + dlc.licenses = []; + + licenseCount = ticket.readUint16(); + + for (j = 0; j < licenseCount; j++) { + dlc.licenses.push(ticket.readUint32()); + } + + details.dlc.push(dlc); + } + + ticket.readUint16(); // reserved + if (ticket.offset + 128 == ticket.limit) { + // Has signature + details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); + } + + let date = new Date(); + details.isExpired = details.ownershipTicketExpires < date; + 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); + return details; + return null; // not a valid ticket + } + + return details; +} -var outer=EncryptedAppTicket.decode(new Buffer("080110a7e59890031815204f2a80014527ad8cccd8cd9b7d3260529556004ddf0454a1c2e8543495b1eb72b3dff44d9d67555798ef44b7e3f97cf86d3bf1bc3f84a91de415c382b0d96033c8e5398f2b775bcb004f5487c651d81ef410d7d2917cbb407713397d2c29a455b6852eac7db1a0e52248585bd5440ef07de54b7519f67eefbad5420d97b77ef7e35450af","hex")); -var decrypted=symmetricDecrypt(outer.encryptedTicket,new Buffer("8af5c487af6c7709ef1a4e6e2275fcc0a90f57ae4319de793fca3d7ed8655520","hex")); +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"), +); 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(); + ownershipTicket.userData = userData.toString(); } console.log(ownershipTicket); - diff --git a/useful/template.live_variables.js b/useful/template.live_variables.js index cc4e57da..c622edd9 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 bf907326..c44d4616 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, +}; From db3f76d12d9494678d9419f25ea33c62bec837a8 Mon Sep 17 00:00:00 2001 From: Telokis Date: Sat, 28 Oct 2023 17:42:47 +0200 Subject: [PATCH 5/8] Switch to tabs instead of spaces --- .vscode/settings.json | 21 +- node/.prettierrc | 3 +- node/precompute.js | 38 +- node/server.js | 26680 ++++++++++++++++++------------------- node/server_functions.js | 9660 +++++++------- node/server_worker.js | 22 +- node/test.js | 300 +- 7 files changed, 18363 insertions(+), 18361 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 13ad47b9..e01df6a0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,13 @@ { - "editor.defaultFormatter": "esbenp.prettier-vscode", - "prettier.configPath": "./node/.prettierrc", - "prettier.printWidth": 120, - "prettier.tabWidth": 2, - "prettier.trailingComma": "all", - "prettier.semi": true, - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - } + "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/node/.prettierrc b/node/.prettierrc index e39f1de3..0bb2a476 100644 --- a/node/.prettierrc +++ b/node/.prettierrc @@ -5,5 +5,6 @@ "trailingComma": "all", "semi": true, "bracketSpacing": true, - "arrowParens": "always" + "arrowParens": "always", + "useTabs": true } \ No newline at end of file diff --git a/node/precompute.js b/node/precompute.js index 13b73ec5..e086d1c9 100644 --- a/node/precompute.js +++ b/node/precompute.js @@ -13,23 +13,23 @@ 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"); - } - 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) + ";"); + 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"); + } + 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 28bf769f..9efe6d64 100644 --- a/node/server.js +++ b/node/server.js @@ -3,25 +3,25 @@ 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: {}, + 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 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, - }, + 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"); @@ -75,145 +75,145 @@ var P = {}; // the game var S = {}; // server data var T = {}; // cosmetics var E = { - schedule: { - time_offset: 0, - dailies: [13, 20], - nightlies: [23], - night: false, - }, + schedule: { + time_offset: 0, + dailies: [13, 20], + nightlies: [23], + night: false, + }, }; // server event data 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, + 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, }; 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, + 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, + calls: 200, // 4 seconds + party: 9, + party_max: 10, }; var W = { - // warning flags - chest: {}, + // 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] - //#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 + 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 }; 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 - // RANDOM - halloween: true, - goblin: false, - goldenbat: 160000, - cutebee: 960000, - hide_and_seek: 0, - // DAILIES - goobrawl: false, - crabxx: false, - abtesting: false, - // NIGHTLIES - icegolem: false, - franky: false, + // 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 + // RANDOM + halloween: true, + goblin: false, + goldenbat: 160000, + cutebee: 960000, + hide_and_seek: 0, + // DAILIES + goobrawl: false, + crabxx: false, + abtesting: false, + // NIGHTLIES + icegolem: false, + franky: false, }; var dailies = ["crabxx", "goobrawl", "abtesting"]; shuffle(dailies); var nightlies = ["icegolem", "franky"]; shuffle(nightlies); if (events.holidayseason) { - events.snowman = 60; + events.snowman = 60; } if (events.valentines) { - events.pinkgoo = 60; + events.pinkgoo = 60; } TIMEO = { - EU: 1, - US: -5, - ASIA: 7, + 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, + 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 @@ -231,12771 +231,12771 @@ port = process.argv[process.argv.length - 1]; E.schedule.time_offset = TIMEO[region]; 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; + 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; } 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; + 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; - } - } + 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"; + 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; - // variables.ip_limit=2+1; - // variables.character_limit=1+1; - luckm *= 1.15; - xpm *= 1.2; - goldm *= 1.25; + is_pvp = true; + // variables.ip_limit=2+1; + // variables.character_limit=1+1; + 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) { - log_trace("init", e); - } - }, - function () { - console.log("#X init_game's create_server call failed"); - }, - ); + 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) { + 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" }); - }, - ); + 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) { - // #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", "+"); - return result; + // #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", "+"); + return result; } 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; + 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) { - 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); - 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); - } - response.writeHead(200); - response.end(output); - } catch (e) { - log_trace("chttp_err", e); - } - }); + 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); + 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); + } + 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] - return char; + 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]; - } - }); - ["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.is_npc) { - // data.id="$"+data.id; - 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]; - }); - 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); - } - } - return data; + 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]; + } + }); + 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) { + // data.id="$"+data.id; + 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]; + }); + 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); + } + } + return data; } 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.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; - } - return data; + 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.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; + } + 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 || {}; - return summary; + 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, - }; + 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) { - restore_state(player); - save_state(player); - player.hp = player.max_hp; - player.mp = player.max_mp; - player.s = {}; - player.c = {}; + restore_state(player); + save_state(player); + 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; - } - player.state.restored = true; - } - delete player.team; - delete player.duel; + 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; + } + player.state.restored = true; + } + delete player.team; + delete player.duel; } 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]]; - 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]]; - var 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); - } 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; - } - }); - // git test - return party; + 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]]; + 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]]; + var 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); + } 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; + } + }); + // 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 }); - }); + 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; - }); + 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", + 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; - } - } + 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; - player.level++; - 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 }); - } - // 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) { - console.log("'Your monster!' logged out ->"); - player.socket.emit("ui_log", "You monster!"); - player.socket.disconnect(); - return; - } - } - 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; - }); - [ - "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 = {}; - } - // 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; 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.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 < def[0]) { - return; - } - if (def[1] == "stat") { - player.monster_stats[def[2]] = (player.monster_stats[def[2]] || 0) + def[3]; - } - }); - } - } - character_slots.forEach(function (slot) { - var current = player.slots[slot]; - if (!current) { - return; - } - var def = G.items[current.name]; - if (!def) { - console.log("#X Undefined item: " + current.name + " (" + slot + ")"); - return; - } - var prop = calculate_item_properties(current, { class: player.type, map: player.map }); - if (prop.class && !prop.class.includes(player.type)) { - return; - } - - apply_stats(player, prop, { no_range: slot == "offhand" && def.type == "weapon" }); - if (prop.attack) { - if (slot == "offhand") { - item_attack += prop.attack * 0.7; - } else { - item_attack += prop.attack; - } - } - if (def.ability) { - player.a[def.ability] = { - attr0: ((player.a[def.ability] && player.a[def.ability].attr0) || 0) + prop.attr0, - attr1: ((player.a[def.ability] && player.a[def.ability].attr1) || 0) + prop.attr1, - }; - } - if (def.aura) { - player.aura[def.aura] = { attr0: prop.attr0 || 0, attr1: prop.attr1 || 0 }; - } - if (slot == "mainhand") { - apply_stats(player, class_def.doublehand[def.wtype] || class_def.mainhand[def.wtype] || {}); - } - if (slot == "offhand") { - apply_stats( - player, - class_def.offhand[def.wtype] || - class_def.offhand[def.type] || { - no_range: !player.slots.mainhand, - }, - ); - } - if (prop.set) { - player.sets[def.set] = (player.sets[def.set] || 0) + 1; - } - }); - for (var set in player.sets) { - var prop = G.sets[set] && G.sets[set][player.sets[set]]; - if (!prop) { - continue; - } - apply_stats(player, prop); - } - for (var condition in player.s) { - var prop = G.conditions[condition]; - apply_stats(player, player.s[condition]); - if (!prop) { - continue; - } - apply_stats(player, prop); - } - apply_stats(player, player.monster_stats); - if ( - player.slots.mainhand && - player.slots.offhand && - G.items[player.slots.mainhand.name].wtype == "stars" && - G.items[player.slots.offhand.name].wtype != "stars" - ) { - item_attack /= 3.0; - } - if (player.slots.cape && player.slots.cape.name == "stealthcape") { - player.stealth = true; - } - item_attack = max(item_attack, 5); - if (player.type == "paladin") { - player.attack += item_attack * (player.str / 20.0 + player.int / 40.0); - } else { - player.attack += item_attack * (player[class_def.main_stat] / 20.0); - } - player.attack += player.a_attack; - if (player.type == "priest") { - player.attack *= 1.6; - } - if (player.type == "warrior") { - player.courage += round(player.str / 30); - } - if (player.type == "priest") { - player.mcourage += round(player.int / 30); - } - if (player.type == "paladin") { - player.pcourage += round(player.str / 30 + player.int / 30); - } - //console.log(player.speed) - //player.speed+=player.dex/20.0+player.str/40.0+min(player.level,40)/4.0+max(0,min(player.level-40,20))/5.0+max(0,player.level-60)/7.0 - player.speed += - min(player.dex, 256) / 32.0 + - min(player.str, 256) / 64.0 + - min(player.level, 40) / 10.0 + - max(0, min(player.level - 40, 20)) / 15.0 + - max(0, min(86, player.level) - 60) / 16.0; - - player.aggro_diff = player.bling / 100 - player.cuteness / 100; - - //console.log(player.speed) - //player.max_hp+=player.str*5+player.vit*player.level/2; //player.str*10.0+player.level*10+player.vit*25 - player.max_hp += player.str * 21 + player.vit * (48 + player.level / 3.0); - player.max_hp = max(1, player.max_hp); - player.max_mp += player.int * 15.0 + player.level * 5; - //player.armor+=player.str/2.0; - player.armor += min(player.str, 160) + max(0, player.str - 160) * 0.25; - player.resistance += min(player.int, 180) + max(0, player.int - 180) * 0.25; - player.frequency += - min(player.level, 80) / 164.0 + - min(160, player.dex) / 640.0 + - max(player.dex - 160, 0) / 925.0 + - player.int / 1575.0; // 120 635 1275 is the original mix - // player.frequency=9000; - player.attack_ms = round(1000.0 / player.frequency); - if (player.last_attack_ms && player.attack_ms != player.last_attack_ms) { - // server_log("skill_timeout_correction: "+player.last_attack_ms+" to "+player.attack_ms+" timeout: "+(player.attack_ms-mssince(player.last.attack))) - player.socket.emit("skill_timeout", { - name: "attack", - ms: player.attack_ms - mssince(player.last.attack), - reason: "attack_ms", - }); - } - player.last_attack_ms = player.attack_ms; - player.mp_cost += - min(player.level, 80) * (player.mp_cost / 10.0) + - player.a_mp_cost + - player.crit * 1.25 + - player.lifesteal * 1.5 + - player.manasteal / 5.0; - if (player.damage_type == "physical") { - player.mp_cost += player.apiercing / 15.0; - } else { - player.mp_cost += player.rpiercing / 15.0; - } - player.mp_cost = max(1, player.mp_cost); - if (!player.hp && !player.rip) { - player.hp = player.max_hp; - player.mp = player.max_mp; - } // used for level-ups - if ((player.gold || 0) <= 0) { - player.gold = 0; - } - player.heal = 0; - if (player.type == "priest") { - player.heal = player.attack; - } - player.output = max(5, player.output); - if (player.output) { - player.attack = (player.attack * player.output) / 100.0; - } - if (player.s.damage_received) { - player.attack += (player.s.damage_received.amount * 4) / 100; - } - ["attack", "heal", "hp", "mp", "max_hp", "max_mp", "range", "mp_cost", "resistance", "armor"].forEach( - function (prop) { - player[prop] = round(player[prop]); - }, - ); - player.hp = max(0, min(player.hp, player.max_hp)); - player.mp = max(0, min(player.mp, player.max_mp)); - - if (player.party && parties[player.party]) { - if (player.party_xp) { - player.xxp += player.party_xp; - } - if (player.party_luck) { - player.xluck += player.party_luck; - } - if (player.party_gold) { - player.xgold += player.party_gold; - } - } - - if (goldm != 1) { - player.xgold += (goldm - 1) * 100.0; - player.xluck += (luckm - 1) * 100.0; - player.xxp += (xpm - 1) * 100.0; - } - player.luckm = 1 + player.xluck / 100.0; - player.xpm = 1 + player.xxp / 100.0; - player.goldm = 1 + player.xgold / 100.0; - ["luckm", "xpm", "goldm"].forEach(function (p) { - player[p] = max(0.01, player[p]); - }); - - if (player.tskin == "konami") { - player.range = 120; - player.frequency += 0.25; - player.goldm *= 0.25; - player.luckm += 0.25; - } - - 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); - - player.tax = - (player.level > 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; - - 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); - } - recalculate_vxy(player); - perfc.cps += 1; + 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; + 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 }); + } + // 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) { + console.log("'Your monster!' logged out ->"); + player.socket.emit("ui_log", "You monster!"); + player.socket.disconnect(); + return; + } + } + 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; + }); + [ + "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 = {}; + } + // 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; 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.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 < def[0]) { + return; + } + if (def[1] == "stat") { + player.monster_stats[def[2]] = (player.monster_stats[def[2]] || 0) + def[3]; + } + }); + } + } + character_slots.forEach(function (slot) { + var current = player.slots[slot]; + if (!current) { + return; + } + var def = G.items[current.name]; + if (!def) { + console.log("#X Undefined item: " + current.name + " (" + slot + ")"); + return; + } + var prop = calculate_item_properties(current, { class: player.type, map: player.map }); + if (prop.class && !prop.class.includes(player.type)) { + return; + } + + apply_stats(player, prop, { no_range: slot == "offhand" && def.type == "weapon" }); + if (prop.attack) { + if (slot == "offhand") { + item_attack += prop.attack * 0.7; + } else { + item_attack += prop.attack; + } + } + if (def.ability) { + player.a[def.ability] = { + attr0: ((player.a[def.ability] && player.a[def.ability].attr0) || 0) + prop.attr0, + attr1: ((player.a[def.ability] && player.a[def.ability].attr1) || 0) + prop.attr1, + }; + } + if (def.aura) { + player.aura[def.aura] = { attr0: prop.attr0 || 0, attr1: prop.attr1 || 0 }; + } + if (slot == "mainhand") { + apply_stats(player, class_def.doublehand[def.wtype] || class_def.mainhand[def.wtype] || {}); + } + if (slot == "offhand") { + apply_stats( + player, + class_def.offhand[def.wtype] || + class_def.offhand[def.type] || { + no_range: !player.slots.mainhand, + }, + ); + } + if (prop.set) { + player.sets[def.set] = (player.sets[def.set] || 0) + 1; + } + }); + for (var set in player.sets) { + var prop = G.sets[set] && G.sets[set][player.sets[set]]; + if (!prop) { + continue; + } + apply_stats(player, prop); + } + for (var condition in player.s) { + var prop = G.conditions[condition]; + apply_stats(player, player.s[condition]); + if (!prop) { + continue; + } + apply_stats(player, prop); + } + apply_stats(player, player.monster_stats); + if ( + player.slots.mainhand && + player.slots.offhand && + G.items[player.slots.mainhand.name].wtype == "stars" && + G.items[player.slots.offhand.name].wtype != "stars" + ) { + item_attack /= 3.0; + } + if (player.slots.cape && player.slots.cape.name == "stealthcape") { + player.stealth = true; + } + item_attack = max(item_attack, 5); + if (player.type == "paladin") { + player.attack += item_attack * (player.str / 20.0 + player.int / 40.0); + } else { + player.attack += item_attack * (player[class_def.main_stat] / 20.0); + } + player.attack += player.a_attack; + if (player.type == "priest") { + player.attack *= 1.6; + } + if (player.type == "warrior") { + player.courage += round(player.str / 30); + } + if (player.type == "priest") { + player.mcourage += round(player.int / 30); + } + if (player.type == "paladin") { + player.pcourage += round(player.str / 30 + player.int / 30); + } + //console.log(player.speed) + //player.speed+=player.dex/20.0+player.str/40.0+min(player.level,40)/4.0+max(0,min(player.level-40,20))/5.0+max(0,player.level-60)/7.0 + player.speed += + min(player.dex, 256) / 32.0 + + min(player.str, 256) / 64.0 + + min(player.level, 40) / 10.0 + + max(0, min(player.level - 40, 20)) / 15.0 + + max(0, min(86, player.level) - 60) / 16.0; + + player.aggro_diff = player.bling / 100 - player.cuteness / 100; + + //console.log(player.speed) + //player.max_hp+=player.str*5+player.vit*player.level/2; //player.str*10.0+player.level*10+player.vit*25 + player.max_hp += player.str * 21 + player.vit * (48 + player.level / 3.0); + player.max_hp = max(1, player.max_hp); + player.max_mp += player.int * 15.0 + player.level * 5; + //player.armor+=player.str/2.0; + player.armor += min(player.str, 160) + max(0, player.str - 160) * 0.25; + player.resistance += min(player.int, 180) + max(0, player.int - 180) * 0.25; + player.frequency += + min(player.level, 80) / 164.0 + + min(160, player.dex) / 640.0 + + max(player.dex - 160, 0) / 925.0 + + player.int / 1575.0; // 120 635 1275 is the original mix + // player.frequency=9000; + player.attack_ms = round(1000.0 / player.frequency); + if (player.last_attack_ms && player.attack_ms != player.last_attack_ms) { + // server_log("skill_timeout_correction: "+player.last_attack_ms+" to "+player.attack_ms+" timeout: "+(player.attack_ms-mssince(player.last.attack))) + player.socket.emit("skill_timeout", { + name: "attack", + ms: player.attack_ms - mssince(player.last.attack), + reason: "attack_ms", + }); + } + player.last_attack_ms = player.attack_ms; + player.mp_cost += + min(player.level, 80) * (player.mp_cost / 10.0) + + player.a_mp_cost + + player.crit * 1.25 + + player.lifesteal * 1.5 + + player.manasteal / 5.0; + if (player.damage_type == "physical") { + player.mp_cost += player.apiercing / 15.0; + } else { + player.mp_cost += player.rpiercing / 15.0; + } + player.mp_cost = max(1, player.mp_cost); + if (!player.hp && !player.rip) { + player.hp = player.max_hp; + player.mp = player.max_mp; + } // used for level-ups + if ((player.gold || 0) <= 0) { + player.gold = 0; + } + player.heal = 0; + if (player.type == "priest") { + player.heal = player.attack; + } + player.output = max(5, player.output); + if (player.output) { + player.attack = (player.attack * player.output) / 100.0; + } + if (player.s.damage_received) { + player.attack += (player.s.damage_received.amount * 4) / 100; + } + ["attack", "heal", "hp", "mp", "max_hp", "max_mp", "range", "mp_cost", "resistance", "armor"].forEach( + function (prop) { + player[prop] = round(player[prop]); + }, + ); + player.hp = max(0, min(player.hp, player.max_hp)); + player.mp = max(0, min(player.mp, player.max_mp)); + + if (player.party && parties[player.party]) { + if (player.party_xp) { + player.xxp += player.party_xp; + } + if (player.party_luck) { + player.xluck += player.party_luck; + } + if (player.party_gold) { + player.xgold += player.party_gold; + } + } + + if (goldm != 1) { + player.xgold += (goldm - 1) * 100.0; + player.xluck += (luckm - 1) * 100.0; + player.xxp += (xpm - 1) * 100.0; + } + player.luckm = 1 + player.xluck / 100.0; + player.xpm = 1 + player.xxp / 100.0; + player.goldm = 1 + player.xgold / 100.0; + ["luckm", "xpm", "goldm"].forEach(function (p) { + player[p] = max(0.01, player[p]); + }); + + if (player.tskin == "konami") { + player.range = 120; + player.frequency += 0.25; + player.goldm *= 0.25; + player.luckm += 0.25; + } + + 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); + + player.tax = + (player.level > 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; + + 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); + } + recalculate_vxy(player); + 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); - } + 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; - }); - 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]) - // if(p in 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); - } - 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); - } - calculate_common_stats(monster); - recalculate_vxy(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) { + // if(G.conditions[name]) + // { + // for(var p in G.conditions[name]) + // if(p in 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); + } + 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); + } + calculate_common_stats(monster); + recalculate_vxy(monster); } function ccms(monster) { - if (Object.keys(monster.s).length) { - calculate_monster_stats(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"; - } 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 (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) { - return "no"; - } - return 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 (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) { + 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]); + 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); + consume(player, num, 1); } function consume_one_by_id(player, id) { - for (var i = 0; i < player.items.length; i++) { - if (player.items[i] && player.items[i].name == id) { - consume_one(player, i); - return true; - } - } - return false; + for (var i = 0; i < player.items.length; i++) { + if (player.items[i] && player.items[i].name == id) { + consume_one(player, i); + return true; + } + } + return false; } function create_new_item(name, quantity) { - var new_item = { name: name }; - if (G.items[name].s) { - new_item.q = quantity || 1; - } - if (G.items[name].upgrade || G.items[name].compound) { - new_item.level = 0; - } - return new_item; + var new_item = { name: name }; + if (G.items[name].s) { + new_item.q = quantity || 1; + } + if (G.items[name].upgrade || G.items[name].compound) { + new_item.level = 0; + } + return new_item; } function aadd_item(player, new_item) { - // admin [24/06/23] - add_item(player, new_item, { announce: false }); - reopen(player, "u+cid+reopen"); + // admin [24/06/23] + add_item(player, new_item, { announce: false }); + reopen(player, "u+cid+reopen"); } function add_item(player, new_item, args) { - // # IMPORTANT: ALWAYS KEEP CAN_ADD_ITEM IN SYNC! - var done = false; - var num = 0; - var a = false; - if (!args) { - args = {}; - } - if (!new_item.name) { - new_item = create_new_item(new_item, args.q || 1); - } - if (args.p) { - new_item.p = args.p; - } - if ( - (args.m || args.r) && - ((G.items[new_item.name].upgrade && Math.random() < 1.0 / 500) || - (G.items[new_item.name].compound && Math.random() < 1.0 / 20000)) - ) { - new_item.p = "shiny"; - a = true; - } - if (G.items[new_item.name].s) { - for (var i = 0; i < player.items.length; i++) { - var item = player.items[i]; - if (can_stack(item, new_item)) { - if ((args.v && is_in_pvp(player, 1)) || new_item.v) { - item.v = new Date(); - } - if (new_item.m) { - item.m = new_item.m; - } - item.q = parseInt((new_item.q || 1) + (item.q || 1)); // to fix the side-effects of not including parseInt on 'sell'/'buy' [22/02/18] - player.citems[i] = cache_item(player.items[i]); - done = true; - num = i; - break; - } - } - } - if (!new_item.oo) { - new_item.oo = player.name; - } - if (!done) { - for (var i = 0; i < player.items.length; i++) { - var item = player.items[i]; - if (!item) { - if (args.v && is_in_pvp(player, 1)) { - new_item.v = new Date(); - } - player.items[i] = new_item; - player.citems[i] = cache_item(player.items[i]); - player.esize--; // esize-- needed for concurrent party chest operations [18/10/18] - done = true; - num = i; - break; - } - } - } - if (!done) { - player.items.push(new_item); - player.citems[player.items.length - 1] = cache_item(player.items[player.items.length - 1]); - num = player.items.length - 1; - player.esize--; - } - if ( - args.m && - player.s.mluck && - get_player(player.s.mluck.f) && - Math.random() <= 0.02 && - can_add_item(get_player(player.s.mluck.f), new_item.name) - ) { - var mr = get_player(player.s.mluck.f); - var item = create_new_item(new_item.name); - item.m = player.name; - if (new_item.data) { - item.data = new_item.data; - } // "cxjar" - var num = add_item(mr, item, { m: 1 }); - mr.socket.emit("game_log", { message: "Found " + item_to_phrase(item), color: "#64B867" }); - xy_emit(mr, "ui", { type: "+M", name: mr.name, item: cache_item(item), num: num, cevent: "mluck", event: "mluck" }); - resend(mr, "reopen+nc+inv"); - } - if (args.announce !== false && (a || (a_score[new_item.name] || 0) < G.items[new_item.name].a) && !player.stealth) { - var event = G.items[new_item.name].event; - var e_type = "server_found"; - var e_phrase = " found "; - if (!args.found) { - e_type = "server_received"; - e_phrase = " received "; - } - if (args.phrase) { - e_type = "server_received"; - e_phrase = " " + args.phrase.toLowerCase() + " "; - } - broadcast("server_message", { - message: player.name + e_phrase + item_to_phrase(new_item), - color: "#85C76B", - type: e_type, - item: cache_item(new_item), - name: player.name, - event: event, - }); - a_score[new_item.name] = (a_score[new_item.name] || 0) + 1.05; - } - if (args.log) { - player.socket.emit("game_response", { response: "add_item", item: new_item }); - } - return num; + // # IMPORTANT: ALWAYS KEEP CAN_ADD_ITEM IN SYNC! + var done = false; + var num = 0; + var a = false; + if (!args) { + args = {}; + } + if (!new_item.name) { + new_item = create_new_item(new_item, args.q || 1); + } + if (args.p) { + new_item.p = args.p; + } + if ( + (args.m || args.r) && + ((G.items[new_item.name].upgrade && Math.random() < 1.0 / 500) || + (G.items[new_item.name].compound && Math.random() < 1.0 / 20000)) + ) { + new_item.p = "shiny"; + a = true; + } + if (G.items[new_item.name].s) { + for (var i = 0; i < player.items.length; i++) { + var item = player.items[i]; + if (can_stack(item, new_item)) { + if ((args.v && is_in_pvp(player, 1)) || new_item.v) { + item.v = new Date(); + } + if (new_item.m) { + item.m = new_item.m; + } + item.q = parseInt((new_item.q || 1) + (item.q || 1)); // to fix the side-effects of not including parseInt on 'sell'/'buy' [22/02/18] + player.citems[i] = cache_item(player.items[i]); + done = true; + num = i; + break; + } + } + } + if (!new_item.oo) { + new_item.oo = player.name; + } + if (!done) { + for (var i = 0; i < player.items.length; i++) { + var item = player.items[i]; + if (!item) { + if (args.v && is_in_pvp(player, 1)) { + new_item.v = new Date(); + } + player.items[i] = new_item; + player.citems[i] = cache_item(player.items[i]); + player.esize--; // esize-- needed for concurrent party chest operations [18/10/18] + done = true; + num = i; + break; + } + } + } + if (!done) { + player.items.push(new_item); + player.citems[player.items.length - 1] = cache_item(player.items[player.items.length - 1]); + num = player.items.length - 1; + player.esize--; + } + if ( + args.m && + player.s.mluck && + get_player(player.s.mluck.f) && + Math.random() <= 0.02 && + can_add_item(get_player(player.s.mluck.f), new_item.name) + ) { + var mr = get_player(player.s.mluck.f); + var item = create_new_item(new_item.name); + item.m = player.name; + if (new_item.data) { + item.data = new_item.data; + } // "cxjar" + var num = add_item(mr, item, { m: 1 }); + mr.socket.emit("game_log", { message: "Found " + item_to_phrase(item), color: "#64B867" }); + xy_emit(mr, "ui", { type: "+M", name: mr.name, item: cache_item(item), num: num, cevent: "mluck", event: "mluck" }); + resend(mr, "reopen+nc+inv"); + } + if (args.announce !== false && (a || (a_score[new_item.name] || 0) < G.items[new_item.name].a) && !player.stealth) { + var event = G.items[new_item.name].event; + var e_type = "server_found"; + var e_phrase = " found "; + if (!args.found) { + e_type = "server_received"; + e_phrase = " received "; + } + if (args.phrase) { + e_type = "server_received"; + e_phrase = " " + args.phrase.toLowerCase() + " "; + } + broadcast("server_message", { + message: player.name + e_phrase + item_to_phrase(new_item), + color: "#85C76B", + type: e_type, + item: cache_item(new_item), + name: player.name, + event: event, + }); + a_score[new_item.name] = (a_score[new_item.name] || 0) + 1.05; + } + if (args.log) { + player.socket.emit("game_response", { response: "add_item", item: new_item }); + } + return num; } function list_to_pseudo_items(list, type, add) { - var items = []; - var ex = {}; - if (!type || type == "multi") { - list.forEach(function (el) { - if (G.items[el[1]].s) { - if (ex[el[1]]) { - ex[el[1]].q += max(1, el[0]); - } else { - ex[el[1]] = { name: el[1], q: max(1, el[0]) }; - items.push(ex[el[1]]); - } - } else { - items.push({ name: el[1], q: max(1, el[0]) }); - } - }); - } else if (type == "chest") { - list.forEach(function (name) { - if (G.items[name].s) { - if (ex[name]) { - ex[name].q += 1; - } else { - ex[name] = create_new_item(name); - items.push(ex[name]); - } - } else { - items.push(create_new_item(name)); - } - }); - (add || []).forEach(function (item) { - items.push(item); - }); - } - return items; + var items = []; + var ex = {}; + if (!type || type == "multi") { + list.forEach(function (el) { + if (G.items[el[1]].s) { + if (ex[el[1]]) { + ex[el[1]].q += max(1, el[0]); + } else { + ex[el[1]] = { name: el[1], q: max(1, el[0]) }; + items.push(ex[el[1]]); + } + } else { + items.push({ name: el[1], q: max(1, el[0]) }); + } + }); + } else if (type == "chest") { + list.forEach(function (name) { + if (G.items[name].s) { + if (ex[name]) { + ex[name].q += 1; + } else { + ex[name] = create_new_item(name); + items.push(ex[name]); + } + } else { + items.push(create_new_item(name)); + } + }); + (add || []).forEach(function (item) { + items.push(item); + }); + } + return items; } function bank_add_item(player, slot, new_item) { - var done = false; - if (!new_item.name) { - new_item = create_new_item(new_item); - } - if (new_item.q) { - for (var i = 0; i < player.user[slot].length; i++) { - var item = player.user[slot][i]; - if (can_stack(item, new_item)) { - item.q = (item.q || 1) + (new_item.q || 1); - player.cuser[slot][i] = cache_item(player.user[slot][i]); - done = true; - break; - } - } - } - if (!done) { - for (var i = 0; i < player.user[slot].length; i++) { - var item = player.user[slot][i]; - if (!item) { - player.user[slot][i] = new_item; - player.cuser[slot][i] = cache_item(player.user[slot][i]); - done = true; - break; - } - } - } - if (!done) { - player.user[slot].push(new_item); - player.cuser[slot][player.user[slot].length - 1] = cache_item(player.user[slot][player.user[slot].length - 1]); - } + var done = false; + if (!new_item.name) { + new_item = create_new_item(new_item); + } + if (new_item.q) { + for (var i = 0; i < player.user[slot].length; i++) { + var item = player.user[slot][i]; + if (can_stack(item, new_item)) { + item.q = (item.q || 1) + (new_item.q || 1); + player.cuser[slot][i] = cache_item(player.user[slot][i]); + done = true; + break; + } + } + } + if (!done) { + for (var i = 0; i < player.user[slot].length; i++) { + var item = player.user[slot][i]; + if (!item) { + player.user[slot][i] = new_item; + player.cuser[slot][i] = cache_item(player.user[slot][i]); + done = true; + break; + } + } + } + if (!done) { + player.user[slot].push(new_item); + player.cuser[slot][player.user[slot].length - 1] = cache_item(player.user[slot][player.user[slot].length - 1]); + } } function drop_item_logic(drop, def, pvp) { - var added = false; - if (def[1] == "shells") { - drop.cash += def[2]; - } else if (def[1] == "cxjar" || def[1] == "emotionjar") { - var item = { name: def[1], q: def[2], data: def[3] }; - if (pvp) { - item.v = new Date(); - } - for (var i = 0; i < drop.items.length; i++) { - if (can_stack(drop.items[i], item)) { - drop.items[i].q += def[2]; - added = true; - break; - } - } - if (!added) { - drop.items.push(item); - } - } else if (def[1] == "open") { - chest_exchange(drop, def[2]); - } else { - var item = create_new_item(def[1]); - if (pvp) { - item.v = new Date(); - } - for (var i = 0; i < drop.items.length; i++) { - if (can_stack(drop.items[i], item)) { - drop.items[i].q += 1; - added = true; - break; - } - } - if (!added) { - drop.items.push(item); - } - } + var added = false; + if (def[1] == "shells") { + drop.cash += def[2]; + } else if (def[1] == "cxjar" || def[1] == "emotionjar") { + var item = { name: def[1], q: def[2], data: def[3] }; + if (pvp) { + item.v = new Date(); + } + for (var i = 0; i < drop.items.length; i++) { + if (can_stack(drop.items[i], item)) { + drop.items[i].q += def[2]; + added = true; + break; + } + } + if (!added) { + drop.items.push(item); + } + } else if (def[1] == "open") { + chest_exchange(drop, def[2]); + } else { + var item = create_new_item(def[1]); + if (pvp) { + item.v = new Date(); + } + for (var i = 0; i < drop.items.length; i++) { + if (can_stack(drop.items[i], item)) { + drop.items[i].q += 1; + added = true; + break; + } + } + if (!added) { + drop.items.push(item); + } + } } function drop_one_thing(player, items, args) { - if (!args) { - args = {}; - } - var drop_id = randomStr(30); - var chest = args.chest || "chest1"; - if (!is_array(items)) { - items = [items]; - } - drop = chests[drop_id] = { items: [], cash: 0 }; - for (var i = 0; i < items.length; i++) { - if (is_string(items[i])) { - drop_item_logic(drop, [1, items[i]], is_in_pvp(player, 1)); - } - } - drop.gold = args.gold || 0; - drop.x = (args.x !== undefined && args.x) || player.x; - drop.y = (args.y !== undefined && args.y) || player.y; - drop.map = args.map || player.map; - drop.date = new Date(); - player.socket.emit("drop", { - x: drop.x, - y: drop.y, - items: drop.items.length, - chest: chest, - id: drop_id, - map: drop.map, - owners: [player.owner], - }); + if (!args) { + args = {}; + } + var drop_id = randomStr(30); + var chest = args.chest || "chest1"; + if (!is_array(items)) { + items = [items]; + } + drop = chests[drop_id] = { items: [], cash: 0 }; + for (var i = 0; i < items.length; i++) { + if (is_string(items[i])) { + drop_item_logic(drop, [1, items[i]], is_in_pvp(player, 1)); + } + } + drop.gold = args.gold || 0; + drop.x = (args.x !== undefined && args.x) || player.x; + drop.y = (args.y !== undefined && args.y) || player.y; + drop.map = args.map || player.map; + drop.date = new Date(); + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.items.length, + chest: chest, + id: drop_id, + map: drop.map, + owners: [player.owner], + }); } function drop_something(player, monster, share) { - if (monster.pet || monster.trap) { - return; - } - achievement_logic_monster_kill(player, monster); - share = (share === undefined && 1) || share || 0; - // console.log("share: "+share); - var drop_id = randomStr(30); - var drop; - var chest = "chest3"; - var hp_mult = 1; - var drop_norm = 1000; - var global_mult = monster.mult; - var monster_mult = monster.mult; // originally: G.maps[player.map] && G.maps[player.map].drop_norm [31/01/18] - var GOLD = D.monster_gold[monster.type]; - if (B.use_pack_golds && monster.gold) { - GOLD = monster.gold; - } - if (drop_norm) { - hp_mult = monster.max_hp / drop_norm; - } - - drop = chests[drop_id] = { items: [], cash: 0 }; - drop.gold = - round(1 + GOLD * D.drops.gold.base * share + Math.random() * GOLD * D.drops.gold.random * share) * - monster.level * - monster.mult || 0; // previously 0.75 - if (monster.extra_gold) { - drop.egold = (drop.egold || 0) + max(0, monster.extra_gold); - } - if (monster.outgoing) { - drop.egold = - (drop.egold || 0) + - min( - monster.outgoing * B.m_outgoing_gmult, - G.monsters[monster.type].hp * 0.048 * (gameplay == "hardcore" ? 50 : 1), - ); - } - if (drop.egold) { - drop.egold *= share; - } - if (monster.difficulty === 0) { - drop.gold = drop.egold = 0; - } - drop.x = monster.x; - drop.y = monster.y; - drop.map = monster.map; - if (monster["global"]) { - drop.x = player.x; - drop.y = player.y; - drop.map = player.map; - } - // if(player.level<50 && mode.low49_20xglobal) global_mult=20; - Commented out after SpadarFaar discovered/used it [16/04/19] - // console.log(global_mult); - if (monster["1hp"]) { - global_mult *= 1000; - } - if (D.drops.maps.global_static && player.tskin != "konami" && B.global_drops) { - D.drops.maps.global_static.forEach(function (item) { - if (Math.random() / share / player.luckm / monster.luckx / global_mult < item[0] || mode.drop_all) { - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - if (D.drops.maps.global && player.tskin != "konami" && B.global_drops) { - D.drops.maps.global.forEach(function (item) { - if (Math.random() / share / player.luckm / hp_mult / monster.luckx / global_mult < item[0] || mode.drop_all) { - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - if (D.drops.maps[monster.map] && player.tskin != "konami") { - D.drops.maps[monster.map].forEach(function (item) { - if (Math.random() / share / player.luckm / hp_mult / monster.luckx < item[0] || mode.drop_all) { - drop_item_logic(drop, item, is_in_pvp(player, 1)); - } - }); - } - // if(player.level<50 && monster.type=="goo" && mode.low49_200xgoo) monster_mult=200; - if (D.drops.monsters[monster.type] && player.tskin != "konami") { - D.drops.monsters[monster.type].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 (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() < D.drops.gold.x10) { - drop.gold *= 10; - chest = "chest4"; - } // previously 12 - if (Math.random() < D.drops.gold.x50) { - drop.gold *= 50; - chest = "chest5"; - } // previously 200 - if (drop.items.length || drop.cash) { - chest = "chest6"; - } - drop.date = new Date(); - if (player.party) { - var owners = []; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - if (current && !owners.includes(current.owner)) { - owners.push(current.owner); - } - }); - party_emit( - player.party, - "drop", - { - x: drop.x, - y: drop.y, - items: drop.items.length, - chest: chest, - id: drop_id, - party: player.party, - map: drop.map, - owners: owners, - }, - { instance: player.in }, - ); - } else { - player.socket.emit("drop", { - x: drop.x, - y: drop.y, - items: drop.items.length, - chest: chest, - id: drop_id, - map: drop.map, - owners: [player.owner], - }); - } + if (monster.pet || monster.trap) { + return; + } + achievement_logic_monster_kill(player, monster); + share = (share === undefined && 1) || share || 0; + // console.log("share: "+share); + var drop_id = randomStr(30); + var drop; + var chest = "chest3"; + var hp_mult = 1; + var drop_norm = 1000; + var global_mult = monster.mult; + var monster_mult = monster.mult; // originally: G.maps[player.map] && G.maps[player.map].drop_norm [31/01/18] + var GOLD = D.monster_gold[monster.type]; + if (B.use_pack_golds && monster.gold) { + GOLD = monster.gold; + } + if (drop_norm) { + hp_mult = monster.max_hp / drop_norm; + } + + drop = chests[drop_id] = { items: [], cash: 0 }; + drop.gold = + round(1 + GOLD * D.drops.gold.base * share + Math.random() * GOLD * D.drops.gold.random * share) * + monster.level * + monster.mult || 0; // previously 0.75 + if (monster.extra_gold) { + drop.egold = (drop.egold || 0) + max(0, monster.extra_gold); + } + if (monster.outgoing) { + drop.egold = + (drop.egold || 0) + + min( + monster.outgoing * B.m_outgoing_gmult, + G.monsters[monster.type].hp * 0.048 * (gameplay == "hardcore" ? 50 : 1), + ); + } + if (drop.egold) { + drop.egold *= share; + } + if (monster.difficulty === 0) { + drop.gold = drop.egold = 0; + } + drop.x = monster.x; + drop.y = monster.y; + drop.map = monster.map; + if (monster["global"]) { + drop.x = player.x; + drop.y = player.y; + drop.map = player.map; + } + // if(player.level<50 && mode.low49_20xglobal) global_mult=20; - Commented out after SpadarFaar discovered/used it [16/04/19] + // console.log(global_mult); + if (monster["1hp"]) { + global_mult *= 1000; + } + if (D.drops.maps.global_static && player.tskin != "konami" && B.global_drops) { + D.drops.maps.global_static.forEach(function (item) { + if (Math.random() / share / player.luckm / monster.luckx / global_mult < item[0] || mode.drop_all) { + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (D.drops.maps.global && player.tskin != "konami" && B.global_drops) { + D.drops.maps.global.forEach(function (item) { + if (Math.random() / share / player.luckm / hp_mult / monster.luckx / global_mult < item[0] || mode.drop_all) { + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + if (D.drops.maps[monster.map] && player.tskin != "konami") { + D.drops.maps[monster.map].forEach(function (item) { + if (Math.random() / share / player.luckm / hp_mult / monster.luckx < item[0] || mode.drop_all) { + drop_item_logic(drop, item, is_in_pvp(player, 1)); + } + }); + } + // if(player.level<50 && monster.type=="goo" && mode.low49_200xgoo) monster_mult=200; + if (D.drops.monsters[monster.type] && player.tskin != "konami") { + D.drops.monsters[monster.type].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 (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() < D.drops.gold.x10) { + drop.gold *= 10; + chest = "chest4"; + } // previously 12 + if (Math.random() < D.drops.gold.x50) { + drop.gold *= 50; + chest = "chest5"; + } // previously 200 + if (drop.items.length || drop.cash) { + chest = "chest6"; + } + drop.date = new Date(); + if (player.party) { + var owners = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && !owners.includes(current.owner)) { + owners.push(current.owner); + } + }); + party_emit( + player.party, + "drop", + { + x: drop.x, + y: drop.y, + items: drop.items.length, + chest: chest, + id: drop_id, + party: player.party, + map: drop.map, + owners: owners, + }, + { instance: player.in }, + ); + } else { + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.items.length, + chest: chest, + id: drop_id, + map: drop.map, + owners: [player.owner], + }); + } } function drop_something_hardcore(player, target) { - var drop_id = randomStr(30); - var drop; - drop = chests[drop_id] = { items: [], pvp_items: [], cash: 0 }; - drop.gold = 99; - drop.x = target.x; - drop.y = target.y + 10; - drop.map = target.map; - - target.slots.elixir = target.cslots.elixir = null; - - for (var name in target.slots) { - if (target.slots[name] && !target.slots[name].b) { - var prob = 0.1; - if (name == "mainhand" || name == "offhand") { - prob = 0.05; - } - if (Math.random() < prob) { - drop.pvp_items.push(target.slots[name]); - target.slots[name] = target.cslots[name] = null; - } - } - } - - for (var i = 0; i < 42; i++) { - if (target.items[i]) { - if (Math.random() < 0.2) { - drop.pvp_items.push(target.items[i]); - target.items[i] = target.citems[i] = null; - } else if (Math.random() < 0.05) { - target.items[i] = target.citems[i] = null; - } - } - } - - drop.date = new Date(); - - if (!drop.pvp_items.length) { - delete chests[drop_id]; - return; - } - - if (player.party) { - var owners = []; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - if (current && !owners.includes(current.owner)) { - owners.push(current.owner); - } - }); - party_emit( - player.party, - "drop", - { - x: drop.x, - y: drop.y, - items: drop.pvp_items.length, - chest: "chest8", - id: drop_id, - party: player.party, - map: target.map, - owners: owners, - }, - { instance: player.in }, - ); - } else { - player.socket.emit("drop", { - x: drop.x, - y: drop.y, - items: drop.pvp_items.length, - chest: "chest8", - id: drop_id, - map: target.map, - owners: [player.owner], - }); - } + var drop_id = randomStr(30); + var drop; + drop = chests[drop_id] = { items: [], pvp_items: [], cash: 0 }; + drop.gold = 99; + drop.x = target.x; + drop.y = target.y + 10; + drop.map = target.map; + + target.slots.elixir = target.cslots.elixir = null; + + for (var name in target.slots) { + if (target.slots[name] && !target.slots[name].b) { + var prob = 0.1; + if (name == "mainhand" || name == "offhand") { + prob = 0.05; + } + if (Math.random() < prob) { + drop.pvp_items.push(target.slots[name]); + target.slots[name] = target.cslots[name] = null; + } + } + } + + for (var i = 0; i < 42; i++) { + if (target.items[i]) { + if (Math.random() < 0.2) { + drop.pvp_items.push(target.items[i]); + target.items[i] = target.citems[i] = null; + } else if (Math.random() < 0.05) { + target.items[i] = target.citems[i] = null; + } + } + } + + drop.date = new Date(); + + if (!drop.pvp_items.length) { + delete chests[drop_id]; + return; + } + + if (player.party) { + var owners = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && !owners.includes(current.owner)) { + owners.push(current.owner); + } + }); + party_emit( + player.party, + "drop", + { + x: drop.x, + y: drop.y, + items: drop.pvp_items.length, + chest: "chest8", + id: drop_id, + party: player.party, + map: target.map, + owners: owners, + }, + { instance: player.in }, + ); + } else { + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.pvp_items.length, + chest: "chest8", + id: drop_id, + map: target.map, + owners: [player.owner], + }); + } } function drop_something_pvp(player, target) { - var drop_id = randomStr(30); - var drop; - drop = chests[drop_id] = { items: [], pvp_items: [], cash: 0, pvp: true }; - drop.gold = 0; - drop.x = target.x; - drop.y = target.y + 10; - drop.map = target.map; - - for (var name in target.slots) { - if (target.slots[name] && target.slots[name].v && !target.slots[name].b) { - var prob = 0.5; - if (name == "mainhand" || name == "offhand") { - prob = 0.3; - } - if (Math.random() < prob) { - drop.pvp_items.push(target.slots[name]); - target.slots[name] = target.cslots[name] = null; - } - } - } - - for (var i = 0; i < 42; i++) { - if (target.items[i] && target.items[i].v) { - var prob = (target.items[i].q && 0.5) || 0.8; - if (Math.random() < prob) { - drop.pvp_items.push(target.items[i]); - target.items[i] = target.citems[i] = null; - } - // else if(Math.random()<0.05) target.items[i]=target.citems[i]=null; - } - } - - drop.date = new Date(); - - if (!drop.pvp_items.length) { - delete chests[drop_id]; - return; - } - - if (player.party) { - var owners = []; - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - if (current && !owners.includes(current.owner)) { - owners.push(current.owner); - } - }); - party_emit( - player.party, - "drop", - { - x: drop.x, - y: drop.y, - items: drop.pvp_items.length, - chest: "chest8", - id: drop_id, - party: player.party, - map: target.map, - owners: owners, - }, - { instance: player.in }, - ); - } else { - player.socket.emit("drop", { - x: drop.x, - y: drop.y, - items: drop.pvp_items.length, - chest: "chest8", - id: drop_id, - map: target.map, - owners: [player.owner], - }); - } + var drop_id = randomStr(30); + var drop; + drop = chests[drop_id] = { items: [], pvp_items: [], cash: 0, pvp: true }; + drop.gold = 0; + drop.x = target.x; + drop.y = target.y + 10; + drop.map = target.map; + + for (var name in target.slots) { + if (target.slots[name] && target.slots[name].v && !target.slots[name].b) { + var prob = 0.5; + if (name == "mainhand" || name == "offhand") { + prob = 0.3; + } + if (Math.random() < prob) { + drop.pvp_items.push(target.slots[name]); + target.slots[name] = target.cslots[name] = null; + } + } + } + + for (var i = 0; i < 42; i++) { + if (target.items[i] && target.items[i].v) { + var prob = (target.items[i].q && 0.5) || 0.8; + if (Math.random() < prob) { + drop.pvp_items.push(target.items[i]); + target.items[i] = target.citems[i] = null; + } + // else if(Math.random()<0.05) target.items[i]=target.citems[i]=null; + } + } + + drop.date = new Date(); + + if (!drop.pvp_items.length) { + delete chests[drop_id]; + return; + } + + if (player.party) { + var owners = []; + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + if (current && !owners.includes(current.owner)) { + owners.push(current.owner); + } + }); + party_emit( + player.party, + "drop", + { + x: drop.x, + y: drop.y, + items: drop.pvp_items.length, + chest: "chest8", + id: drop_id, + party: player.party, + map: target.map, + owners: owners, + }, + { instance: player.in }, + ); + } else { + player.socket.emit("drop", { + x: drop.x, + y: drop.y, + items: drop.pvp_items.length, + chest: "chest8", + id: drop_id, + map: target.map, + owners: [player.owner], + }); + } } 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 }); - } - }); - } + 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 - } - 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; - } - return score; + 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 + } + 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; + } + 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]); - } - } - 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 < 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"); - } - } + 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 < 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"); + } + } } 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 < monster.cbuff.length; i++) { - if (player.level <= monster.cbuff[i][0] && G.conditions[monster.cbuff[i][1]]) { - player.s[monster.cbuff[i][1]] = { ms: G.conditions[monster.cbuff[i][1]].duration }; - break; - } - } - } - var score = calculate_monster_score(player, monster); - player.p.stats.monsters[monster.type] = (player.p.stats.monsters[monster.type] || 0) + 1; - player.p.stats.monsters_diff[monster.type] = (player.p.stats.monsters_diff[monster.type] || 0) + (score - 1); - monster_hunt_logic(player, monster); - if (player.type == "merchant") { - return; - } - player.xp += monster.xp * player.xpm * monster.mult; - if (player.t) { - player.t.xp += monster.xp * player.xpm * monster.mult; - } - player.cid++; - player.u = true; - calculate_player_stats(player); - } else { - var xp = monster.xp; - // xp*=[1,1,0.70,0.5,0.4,0.32,0.26,0.24,0.24,0.24,0.24,0.24][parties[player.party].length]; - // original: [1,1,0.8,0.7,0.65,0.5,0.4,0.3,0.3,0.3,0.3,0.3] - // xp=round(xp); - parties[player.party].forEach(function (name) { - var current = players[name_to_id[name]]; - var cxp = round(xp * current.xpm * current.share); - if (monster.rbuff && G.conditions[monster.rbuff]) { - current.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; - } - var score = calculate_monster_score(current, monster); - 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); - if (current.type == "merchant") { - return; - } - current.xp += cxp * monster.mult; - if (current.t) { - current.t.xp += cxp * monster.mult; - } - current.cid++; - current.u = true; - calculate_player_stats(current); - if (current != player) { - current.socket.emit("player", player_to_client(current)); - } - // current.socket.emit("game_log",{message:xp+" XP",color:"#416F3A"}); - disappearing_text(current.socket, current, "+" + cxp, { - map: current.map, - color: "#753D8C", - size: "large", - nv: 1, - }); //party:player.party, - better send it to individuals and multiply on client with a setting [19/08/16] - }); - } + 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 < monster.cbuff.length; i++) { + if (player.level <= monster.cbuff[i][0] && G.conditions[monster.cbuff[i][1]]) { + player.s[monster.cbuff[i][1]] = { ms: G.conditions[monster.cbuff[i][1]].duration }; + break; + } + } + } + var score = calculate_monster_score(player, monster); + player.p.stats.monsters[monster.type] = (player.p.stats.monsters[monster.type] || 0) + 1; + player.p.stats.monsters_diff[monster.type] = (player.p.stats.monsters_diff[monster.type] || 0) + (score - 1); + monster_hunt_logic(player, monster); + if (player.type == "merchant") { + return; + } + player.xp += monster.xp * player.xpm * monster.mult; + if (player.t) { + player.t.xp += monster.xp * player.xpm * monster.mult; + } + player.cid++; + player.u = true; + calculate_player_stats(player); + } else { + var xp = monster.xp; + // xp*=[1,1,0.70,0.5,0.4,0.32,0.26,0.24,0.24,0.24,0.24,0.24][parties[player.party].length]; + // original: [1,1,0.8,0.7,0.65,0.5,0.4,0.3,0.3,0.3,0.3,0.3] + // xp=round(xp); + parties[player.party].forEach(function (name) { + var current = players[name_to_id[name]]; + var cxp = round(xp * current.xpm * current.share); + if (monster.rbuff && G.conditions[monster.rbuff]) { + current.s[monster.rbuff] = { ms: G.conditions[monster.rbuff].duration }; + } + var score = calculate_monster_score(current, monster); + 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); + if (current.type == "merchant") { + return; + } + current.xp += cxp * monster.mult; + if (current.t) { + current.t.xp += cxp * monster.mult; + } + current.cid++; + current.u = true; + calculate_player_stats(current); + if (current != player) { + current.socket.emit("player", player_to_client(current)); + } + // current.socket.emit("game_log",{message:xp+" XP",color:"#416F3A"}); + disappearing_text(current.socket, current, "+" + cxp, { + map: current.map, + color: "#753D8C", + size: "large", + nv: 1, + }); //party:player.party, - better send it to individuals and multiply on client with a setting [19/08/16] + }); + } } function kill_monster(attacker, target) { - if (target.dead) { - return; - } // [04/02/23] - var no_decrease = false; - if (attacker) { - if (!attacker.party) { - attacker.socket.emit("game_log", "You " + killed_message(target.type)); - } // tut("killagoo") on game.js - else { - party_emit(attacker.party, "game_log", attacker.name + " " + killed_message(target.type)); - } - if (B.free_last_hits && target.target && target.target != attacker.name) { - stop_pursuit(target, { force: true, cause: "kill_monster call" }); - } - if (!target.target) { - target_player(target, attacker, 1); - no_decrease = true; - } - } - issue_monster_award(target); - remove_monster(target, { no_decrease: no_decrease }); + if (target.dead) { + return; + } // [04/02/23] + var no_decrease = false; + if (attacker) { + if (!attacker.party) { + attacker.socket.emit("game_log", "You " + killed_message(target.type)); + } // tut("killagoo") on game.js + else { + party_emit(attacker.party, "game_log", attacker.name + " " + killed_message(target.type)); + } + if (B.free_last_hits && target.target && target.target != attacker.name) { + stop_pursuit(target, { force: true, cause: "kill_monster call" }); + } + if (!target.target) { + target_player(target, attacker, 1); + no_decrease = true; + } + } + issue_monster_award(target); + remove_monster(target, { no_decrease: no_decrease }); } function player_rip_logic(player) { - if (!player.rip && player.hp <= 0) { - defeat_player(player); - rip(player); - } + if (!player.rip && player.hp <= 0) { + defeat_player(player); + rip(player); + } } function pwn_routine(victor, target) { - issue_player_award(victor, target); - rip(target); - instance_emit(target.in, "server_message", { - message: victor.name + " pwned " + target.name, - color: (victor.team && colors[victor.team]) || "gray", - }); - if (target.map == "arena") { - xy_emit(npcs.pvp, "chat_log", { owner: npcs.pvp.name, message: victor.name + " pwned " + target.name, id: "pvp" }); - } + issue_player_award(victor, target); + rip(target); + instance_emit(target.in, "server_message", { + message: victor.name + " pwned " + target.name, + color: (victor.team && colors[victor.team]) || "gray", + }); + if (target.map == "arena") { + xy_emit(npcs.pvp, "chat_log", { owner: npcs.pvp.name, message: victor.name + " pwned " + target.name, id: "pvp" }); + } } function issue_player_award(attacker, target) { - if (attacker.map == "duelland") { - if (target.duel) { - duel_defeat(target); - } - return; - } - if (attacker.map == "abtesting" && attacker.team != target.team) { - if (E.abtesting) { - E.abtesting[attacker.team]++; - item_achievement_increment(attacker, attacker.slots.orb, "abtesting"); - // instance_emit(attacker.map,"game_event",{name:"ab_score",A:E.abtesting.A,B:E.abtesting.B,win:attacker.name,lose:target.name,color:attacker.team}); - broadcast_e(); - } - return; - } - var lost_xp = floor(min(max((target.max_xp * 0.01) / 10, (target.xp * 0.02) / 10), target.xp)); - - 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 (G.maps[attacker.map].safe_pvp && !is_pvp) { - lost_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); - } - - 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 (!is_same(attacker, target, 1)) { - attacker.kills++; - } - - 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]; - - target.socket.emit("game_log", { message: "Slain by " + attacker.name, color: "#F12F02" }); - var lost_shells = 0; - var psize = 1; - if (lost_xp) { - for (var i = 0; i < target.isize; i++) { - if (target.items[i] && target.items[i].name == "xptome") { - lost_shells = 2; - lost_xp = floor(lost_xp / 50); - consume_one(target, i); - target.to_reopen = true; - target.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); - break; - } - } - } - - target.gold -= lost_gold; - target.xp -= lost_xp; - if (target.xp < 0) { - target.xp = 0; - } - target.socket.emit("game_log", "Lost " + to_pretty_num(lost_gold) + " gold"); - target.socket.emit("game_log", "Lost " + to_pretty_num(lost_xp) + " experience"); - target.socket.emit("disappearing_text", { - message: "-" + lost_xp, - x: target.x, - y: target.y - 30, - args: { color: colors.party_xp }, - }); - - if (gameplay == "hardcore") { - drop_something_hardcore(attacker, target); - } - if (is_in_pvp(target)) { - drop_something_pvp(attacker, target); - } - - if (!attacker.party) { - if (attacker.type == "merchant") { - lost_xp = 0; - } - attacker.gold += gain_gold; - attacker.xp += round(lost_xp * 0.95); - attacker.socket.emit("game_log", { message: "Pwned " + target.name, color: "#67C051" }); - attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); - attacker.socket.emit("disappearing_text", { - message: "+" + gain_gold, - x: target.x, - y: target.y - 40, - args: { color: "+gold", size: "large" }, - }); - attacker.socket.emit("game_log", "Gained " + to_pretty_num(round(lost_xp * 0.95)) + " experience"); - attacker.socket.emit("disappearing_text", { - message: "+" + lost_xp, - x: target.x, - y: target.y - 30, - args: { color: colors.party_xp }, - }); - if (lost_shells) { - add_shells(attacker, lost_shells, "xptome"); - } - } else { - var name = attacker.name; - lost_xp = floor((lost_xp * 0.92) / parties[attacker.party].length); - gain_gold = floor(gain_gold / parties[attacker.party].length); - if (lost_shells) { - add_shells(attacker, lost_shells, "xptome"); - } - // ,lost_shells=ceil(lost_shells/parties[attacker.party].length) - parties[attacker.party].forEach(function (a_name) { - var attacker = players[name_to_id[a_name]]; - attacker.gold += gain_gold; - if (attacker.type != "merchant") { - attacker.xp += lost_xp; - } - attacker.socket.emit("game_log", { message: name + " pwned " + target.name, color: "#67C051" }); - attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); - attacker.socket.emit("disappearing_text", { - message: "+" + gain_gold, - x: target.x, - y: target.y - 40, - args: { color: "+gold", size: "large" }, - }); - if (attacker.type != "merchant") { - attacker.socket.emit("game_log", "Gained " + to_pretty_num(lost_xp) + " experience"); - attacker.socket.emit("disappearing_text", { - message: "+" + lost_xp, - x: target.x, - y: target.y - 30, - args: { color: colors.party_xp }, - }); - } - }); - } + if (attacker.map == "duelland") { + if (target.duel) { + duel_defeat(target); + } + return; + } + if (attacker.map == "abtesting" && attacker.team != target.team) { + if (E.abtesting) { + E.abtesting[attacker.team]++; + item_achievement_increment(attacker, attacker.slots.orb, "abtesting"); + // instance_emit(attacker.map,"game_event",{name:"ab_score",A:E.abtesting.A,B:E.abtesting.B,win:attacker.name,lose:target.name,color:attacker.team}); + broadcast_e(); + } + return; + } + var lost_xp = floor(min(max((target.max_xp * 0.01) / 10, (target.xp * 0.02) / 10), target.xp)); + + 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 (G.maps[attacker.map].safe_pvp && !is_pvp) { + lost_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); + } + + 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 (!is_same(attacker, target, 1)) { + attacker.kills++; + } + + 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]; + + target.socket.emit("game_log", { message: "Slain by " + attacker.name, color: "#F12F02" }); + var lost_shells = 0; + var psize = 1; + if (lost_xp) { + for (var i = 0; i < target.isize; i++) { + if (target.items[i] && target.items[i].name == "xptome") { + lost_shells = 2; + lost_xp = floor(lost_xp / 50); + consume_one(target, i); + target.to_reopen = true; + target.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); + break; + } + } + } + + target.gold -= lost_gold; + target.xp -= lost_xp; + if (target.xp < 0) { + target.xp = 0; + } + target.socket.emit("game_log", "Lost " + to_pretty_num(lost_gold) + " gold"); + target.socket.emit("game_log", "Lost " + to_pretty_num(lost_xp) + " experience"); + target.socket.emit("disappearing_text", { + message: "-" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); + + if (gameplay == "hardcore") { + drop_something_hardcore(attacker, target); + } + if (is_in_pvp(target)) { + drop_something_pvp(attacker, target); + } + + if (!attacker.party) { + if (attacker.type == "merchant") { + lost_xp = 0; + } + attacker.gold += gain_gold; + attacker.xp += round(lost_xp * 0.95); + attacker.socket.emit("game_log", { message: "Pwned " + target.name, color: "#67C051" }); + attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); + attacker.socket.emit("disappearing_text", { + message: "+" + gain_gold, + x: target.x, + y: target.y - 40, + args: { color: "+gold", size: "large" }, + }); + attacker.socket.emit("game_log", "Gained " + to_pretty_num(round(lost_xp * 0.95)) + " experience"); + attacker.socket.emit("disappearing_text", { + message: "+" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); + if (lost_shells) { + add_shells(attacker, lost_shells, "xptome"); + } + } else { + var name = attacker.name; + lost_xp = floor((lost_xp * 0.92) / parties[attacker.party].length); + gain_gold = floor(gain_gold / parties[attacker.party].length); + if (lost_shells) { + add_shells(attacker, lost_shells, "xptome"); + } + // ,lost_shells=ceil(lost_shells/parties[attacker.party].length) + parties[attacker.party].forEach(function (a_name) { + var attacker = players[name_to_id[a_name]]; + attacker.gold += gain_gold; + if (attacker.type != "merchant") { + attacker.xp += lost_xp; + } + attacker.socket.emit("game_log", { message: name + " pwned " + target.name, color: "#67C051" }); + attacker.socket.emit("game_log", "Looted " + to_pretty_num(gain_gold) + " gold"); + attacker.socket.emit("disappearing_text", { + message: "+" + gain_gold, + x: target.x, + y: target.y - 40, + args: { color: "+gold", size: "large" }, + }); + if (attacker.type != "merchant") { + attacker.socket.emit("game_log", "Gained " + to_pretty_num(lost_xp) + " experience"); + attacker.socket.emit("disappearing_text", { + message: "+" + lost_xp, + x: target.x, + y: target.y - 30, + args: { color: colors.party_xp }, + }); + } + }); + } } function commence_attack(attacker, target, atype) { - var attack = attacker.attack; - var mp_cost = 0; - var info = { - apiercing: 0, - damage_type: attacker.damage_type, - heal: false, - lines: true, - positive: false, - first_attack: 0, - procs: false, - conditions: [], - }; // server projectile - - if (!G.skills[atype].hostile) { - info.positive = true; - } - - // TARGET CHANGE - if (attacker.is_player) { - attacker.target = target.id; - } - - // FAILURE SCENARIOS - if ( - attacker.type == "merchant" && - !G.skills[atype].merchant_use && - !(atype == "attack" && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].wtype == "dartgun") - ) { - attacker.socket.emit("game_response", { response: "attack_failed", id: target.id }); - return { failed: true, reason: "merchant", 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); - 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; - } - - // 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; - } - - // PROCS - 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 }; - } - - // 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; - } - - // 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 (info.procs && attacker.s.poisonous) { - info.conditions.push("poisoned"); - } - - 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 ( - (!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); - - 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; - } - 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 (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() < (attacker.a.freeze.attr0 * (G.maps[attacker.map].freeze_multiplier || 1)) / 100.0 - ) { - info.conditions.push("frozen"); - } - if ( - info.procs && - attacker.a.burn && - info.procs && - Math.random() < (attacker.a.burn.attr0 * (G.maps[attacker.map].burn_multiplier || 1)) / 100.0 - ) { - info.conditions.push("burned"); - } - if (info.procs && attacker.a.weave && info.procs) { - info.conditions.push("woven"); - } - if (info.procs && attacker.stun && Math.random() < attacker.stun / 100.0 && info.damage_type == "physical") { - info.conditions.push("stunned"); - } - - var pid = randomStr(6); - var eta = 0; - info.first_attack = info.attack = attack; - info.def = def; - def.damage_type = info.damage_type; - def.pid = pid; - info.attacker = attacker; - info.target = target; - info.atype = atype; - projectiles[pid] = info; - - var action = { - attacker: attacker.id, - target: target.id, - type: atype, - source: atype, - x: target.x, - y: target.y, - eta: 400, - m: target.m, - pid: pid, - }; - - if (def.projectile) { - action.projectile = def.projectile; - if (target.is_monster && G.monsters[target.type].escapist) { - var dampened = false; - for (var id in instances[target.in].monsters) { - var m = instances[target.in].monsters[id]; - if (m.type == "fieldgen0" && point_distance(target.x, target.y, m.x, m.y) < 300) { - target.s.dampened = { ms: 2000 }; - add_condition(target, "dampened", { ms: 2000 }); - dampened = true; - } - } - if (!dampened) { - port_monster(target, random_place(target.map)); - } - } - } - - if (!(G.projectiles[def.projectile] && G.projectiles[def.projectile].instant)) { - eta = (1000 * dist) / G.projectiles[def.projectile].speed; - } else { - action.instant = true; - } - - info.eta = future_ms(eta); - - if (info.heal) { - action.heal = attack; - } else if (attack) { - action.damage = attack; - } - if (info.positive) { - action.positive = true; - } - if (info.conditions.length) { - action.conditions = info.conditions; - } - info.action = action; - xy_emit(attacker, "action", action, target.id); - - if (!eta) { - projectiles_loop(); - } - - action.response = "data"; - action.place = atype; - - return action; + var attack = attacker.attack; + var mp_cost = 0; + var info = { + apiercing: 0, + damage_type: attacker.damage_type, + heal: false, + lines: true, + positive: false, + first_attack: 0, + procs: false, + conditions: [], + }; // server projectile + + if (!G.skills[atype].hostile) { + info.positive = true; + } + + // TARGET CHANGE + if (attacker.is_player) { + attacker.target = target.id; + } + + // FAILURE SCENARIOS + if ( + attacker.type == "merchant" && + !G.skills[atype].merchant_use && + !(atype == "attack" && attacker.slots.mainhand && G.items[attacker.slots.mainhand.name].wtype == "dartgun") + ) { + attacker.socket.emit("game_response", { response: "attack_failed", id: target.id }); + return { failed: true, reason: "merchant", 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); + 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; + } + + // 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; + } + + // PROCS + 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 }; + } + + // 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; + } + + // 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 (info.procs && attacker.s.poisonous) { + info.conditions.push("poisoned"); + } + + 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 ( + (!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); + + 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; + } + 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 (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() < (attacker.a.freeze.attr0 * (G.maps[attacker.map].freeze_multiplier || 1)) / 100.0 + ) { + info.conditions.push("frozen"); + } + if ( + info.procs && + attacker.a.burn && + info.procs && + Math.random() < (attacker.a.burn.attr0 * (G.maps[attacker.map].burn_multiplier || 1)) / 100.0 + ) { + info.conditions.push("burned"); + } + if (info.procs && attacker.a.weave && info.procs) { + info.conditions.push("woven"); + } + if (info.procs && attacker.stun && Math.random() < attacker.stun / 100.0 && info.damage_type == "physical") { + info.conditions.push("stunned"); + } + + var pid = randomStr(6); + var eta = 0; + info.first_attack = info.attack = attack; + info.def = def; + def.damage_type = info.damage_type; + def.pid = pid; + info.attacker = attacker; + info.target = target; + info.atype = atype; + projectiles[pid] = info; + + var action = { + attacker: attacker.id, + target: target.id, + type: atype, + source: atype, + x: target.x, + y: target.y, + eta: 400, + m: target.m, + pid: pid, + }; + + if (def.projectile) { + action.projectile = def.projectile; + if (target.is_monster && G.monsters[target.type].escapist) { + var dampened = false; + for (var id in instances[target.in].monsters) { + var m = instances[target.in].monsters[id]; + if (m.type == "fieldgen0" && point_distance(target.x, target.y, m.x, m.y) < 300) { + target.s.dampened = { ms: 2000 }; + add_condition(target, "dampened", { ms: 2000 }); + dampened = true; + } + } + if (!dampened) { + port_monster(target, random_place(target.map)); + } + } + } + + if (!(G.projectiles[def.projectile] && G.projectiles[def.projectile].instant)) { + eta = (1000 * dist) / G.projectiles[def.projectile].speed; + } else { + action.instant = true; + } + + info.eta = future_ms(eta); + + if (info.heal) { + action.heal = attack; + } else if (attack) { + action.damage = attack; + } + if (info.positive) { + action.positive = true; + } + if (info.conditions.length) { + action.conditions = info.conditions; + } + info.action = action; + xy_emit(attacker, "action", action, target.id); + + if (!eta) { + projectiles_loop(); + } + + action.response = "data"; + action.place = atype; + + return action; } 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; - } - 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.is_player && attack > 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.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"); - } - - 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() * 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 (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; - } - 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 (combo > 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) < radius) { - targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); - } - } - } - if (attacker.is_player) { - for (var id in instances[target.in].monsters) { - var target = instances[target.in].monsters[id]; - if (target.avoidance && Math.random() * 100 < target.avoidance) { - xy_emit( - info.action, - "hit", - { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, miss: true }, - attacker.id, - ); - continue; - } - if (target.id != otarget.id && distance(target, otarget) < radius) { - targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); - } - } - } - } - - targets.forEach(function (target_def) { - delete def.splash; - delete def.dreturn; - delete def.kill; - delete def.unintentional; - - var target = target_def[0]; - if (target_def[1] != "normal") { - def.unintentional = true; - } - if (target_def[1] == "splash") { - def.splash = true; - } - - def.id = target.id; - - if (info.heal) { - if (first) { - o_attack = attack = -ceil( - B.heal_multiplier * - attack * - (0.9 + Math.random() * 0.2) * - damage_multiplier(((target[defense] || 0) - (attacker[pierce] || 0)) / 2.0), - ); - if (target.s.poisoned) { - attack = round(attack * 0.25); - } - } else { - attack = o_attack; - } - def.heal = -attack; - if (attacker.is_player && target.type == "ghost" && !target.s.healed) { - target.s.healed = { ms: 960 * 60 * 60 * 1000 }; - drop_one_thing(attacker, "essenceoflife", { x: target.x, y: target.y, chest: "chestp" }); - } - if (G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.is_player) { - var gold = -88; - target.gold -= gold; - def.goldsteal = gold; - } - } else { - if (evade) { - return; - } - target.hits++; - if (first) { - if (info.crit && Math.random() * 100 < info.crit) { - var cmult = 2 + (info.critdamage || 0) / 100.0; - def.crit = cmult; - attack *= cmult; - } - if (attacker.type == "rogue") { - var maxd = G.skills.stack.max; - // if(G.monsters[target.type] && G.monsters[target.type].stationary) maxd=9999999999; - target.s.stack = { ms: 10000, s: min(maxd, (target.s.stack && target.s.stack.s + 1) || 1) }; - attack += target.s.stack.s; - } - if (def.purify) { - for (var name in target.s) { - if ( - (G.conditions[name] && - (G.conditions[name].buff || G.conditions[name].debuff) && - !G.conditions[name].persistent) || - target.s[name].citizens - ) { - delete target.s[name]; - attack += 400; - info.first_attack += 400; - target.cid++; - target.u = true; - } - } - } - var dmg_mult = 1; - if (attacker.is_player && target["for"]) { - dmg_mult = damage_multiplier(target["for"] * 5); - } - i_attack = attack = ceil(combo_m * attack * (0.9 + ((attack && Math.random() * 0.2) || 0))); - attack = - ceil( - attack * dmg_mult * damage_multiplier((target[defense] || 0) - (attacker[pierce] || 0) - info.apiercing), - ) || 0; - - if (target.incdmgamp) { - attack = round((attack * (100 + target.incdmgamp)) / 100.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); - - if (info.conditions.includes("frozen") && !target.immune && target.hp > 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("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; - } - 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 (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); - } 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 original = target.hp; - if (target.s.mshield && target.mp > 200 && attack > 0) { - // console.log("HERE MPSHIELD!") - var max_mp = target.mp - 200; - var 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 < 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; - } - 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.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); - } // "heal" - else { - add_pdps(attacker, target, -net * B.dps_heal_mult); - } - 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 (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() * 100 < target.a.secondchance.attr0) { - target.hp = target.max_hp; - disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); - } else { - if (target.s.block && get_player(target.s.block.f)) { - pwn_routine(get_player(target.s.block.f), target); - } else { - defeated_by_a_monster(attacker, target); - } - } - } - target.c = {}; - } else if (target.is_monster) { - //player attacks monster - achievement_logic_monster_damage(attacker, target, net); - target.u = true; - target.cid++; - ccms(target); - if (target.hp <= 0) { - if (atype == "mentalburst") { - attacker.mp += net; - } - achievement_logic_monster_last_hit(attacker, target); - kill_monster(attacker, target); - } else { - if (target.a.warp_on_hit && Math.random() < target.a.warp_on_hit.attr0 && !is_disabled(target)) { - var point = random_place(target.map); - transport_monster_to(target, target.in, target.map, point.x, point.y); - } - if (target.drop_on_hit) { - drop_something(attacker, target, 1); - } - if ( - !attacker.is_npc && - !target.target && - !evade && - atype != "shadowstrike" && - !G.monsters[target.type].passive - ) { - target_player(target, attacker); - } - } - } else if (target.is_player) { - //player attacks player - if (!info.positive && !target.s.invincible) { - if (mode.dpvpblock) { - attacker.socket.emit("eval", { code: "pvp_timeout(3600,1)" }); - attacker.s.block = { ms: 3600, f: (attacker.s.block && attacker.s.block.f) || target.name }; - change = true; - } - target.socket.emit("eval", { code: "pvp_timeout(3600)" }); - target.s.block = { ms: 3600, f: (target.s.block && target.s.block.f) || attacker.name }; - if (!is_same(target, attacker, 1)) { - target.s.block.f = attacker.name; - } - target.c = {}; - } - - if (target.hp <= 0 && !target.rip) { - if (atype == "mentalburst") { - attacker.mp += net; - change = true; - } - if (target.a && target.a.secondchance && Math.random() * 100 < target.a.secondchance.attr0) { - target.hp = target.max_hp; - disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); - } else { - var victor = attacker; - if (target.s.block && get_player(target.s.block.f)) { - victor = get_player(target.s.block.f); - } - pwn_routine(victor, target); - } - } - } - - first = false; - - if (target.is_player) { - resend(target, "u+cid"); - } - }); - - if (attacker.hp <= 0 && !attacker.dead && !attacker.rip) { - // dreturn - if (attacker.is_monster) { - kill_monster((attacker.target && get_player(attacker.target)) || target, attacker); - } // monster to player - else if (target.is_monster) { - defeated_by_a_monster(target, attacker); - change = true; - } // player to monster - else { - pwn_routine(target, attacker); - } // player to player - } - - if ((change || attacker.to_resend) && attacker.is_player) { - resend(attacker, "u+cid"); - } - if (change && attacker.is_monster && !attacker.dead) { - attacker.u = true; - attacker.cid++; - ccms(attacker); - } + 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; + } + 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.is_player && attack > 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.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"); + } + + 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() * 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 (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; + } + 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 (combo > 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) < radius) { + targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); + } + } + } + if (attacker.is_player) { + for (var id in instances[target.in].monsters) { + var target = instances[target.in].monsters[id]; + if (target.avoidance && Math.random() * 100 < target.avoidance) { + xy_emit( + info.action, + "hit", + { pid: def.pid, hid: attacker.id, id: target.id, damage: 0, miss: true }, + attacker.id, + ); + continue; + } + if (target.id != otarget.id && distance(target, otarget) < radius) { + targets.push([target, "splash", (damage_multiplier(target[defense] || 0) * intensity) / 100.0]); + } + } + } + } + + targets.forEach(function (target_def) { + delete def.splash; + delete def.dreturn; + delete def.kill; + delete def.unintentional; + + var target = target_def[0]; + if (target_def[1] != "normal") { + def.unintentional = true; + } + if (target_def[1] == "splash") { + def.splash = true; + } + + def.id = target.id; + + if (info.heal) { + if (first) { + o_attack = attack = -ceil( + B.heal_multiplier * + attack * + (0.9 + Math.random() * 0.2) * + damage_multiplier(((target[defense] || 0) - (attacker[pierce] || 0)) / 2.0), + ); + if (target.s.poisoned) { + attack = round(attack * 0.25); + } + } else { + attack = o_attack; + } + def.heal = -attack; + if (attacker.is_player && target.type == "ghost" && !target.s.healed) { + target.s.healed = { ms: 960 * 60 * 60 * 1000 }; + drop_one_thing(attacker, "essenceoflife", { x: target.x, y: target.y, chest: "chestp" }); + } + if (G.monsters[attacker.type] && G.monsters[attacker.type].goldsteal && target.is_player) { + var gold = -88; + target.gold -= gold; + def.goldsteal = gold; + } + } else { + if (evade) { + return; + } + target.hits++; + if (first) { + if (info.crit && Math.random() * 100 < info.crit) { + var cmult = 2 + (info.critdamage || 0) / 100.0; + def.crit = cmult; + attack *= cmult; + } + if (attacker.type == "rogue") { + var maxd = G.skills.stack.max; + // if(G.monsters[target.type] && G.monsters[target.type].stationary) maxd=9999999999; + target.s.stack = { ms: 10000, s: min(maxd, (target.s.stack && target.s.stack.s + 1) || 1) }; + attack += target.s.stack.s; + } + if (def.purify) { + for (var name in target.s) { + if ( + (G.conditions[name] && + (G.conditions[name].buff || G.conditions[name].debuff) && + !G.conditions[name].persistent) || + target.s[name].citizens + ) { + delete target.s[name]; + attack += 400; + info.first_attack += 400; + target.cid++; + target.u = true; + } + } + } + var dmg_mult = 1; + if (attacker.is_player && target["for"]) { + dmg_mult = damage_multiplier(target["for"] * 5); + } + i_attack = attack = ceil(combo_m * attack * (0.9 + ((attack && Math.random() * 0.2) || 0))); + attack = + ceil( + attack * dmg_mult * damage_multiplier((target[defense] || 0) - (attacker[pierce] || 0) - info.apiercing), + ) || 0; + + if (target.incdmgamp) { + attack = round((attack * (100 + target.incdmgamp)) / 100.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); + + if (info.conditions.includes("frozen") && !target.immune && target.hp > 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("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; + } + 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 (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); + } 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 original = target.hp; + if (target.s.mshield && target.mp > 200 && attack > 0) { + // console.log("HERE MPSHIELD!") + var max_mp = target.mp - 200; + var 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 < 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; + } + 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.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); + } // "heal" + else { + add_pdps(attacker, target, -net * B.dps_heal_mult); + } + 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 (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() * 100 < target.a.secondchance.attr0) { + target.hp = target.max_hp; + disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); + } else { + if (target.s.block && get_player(target.s.block.f)) { + pwn_routine(get_player(target.s.block.f), target); + } else { + defeated_by_a_monster(attacker, target); + } + } + } + target.c = {}; + } else if (target.is_monster) { + //player attacks monster + achievement_logic_monster_damage(attacker, target, net); + target.u = true; + target.cid++; + ccms(target); + if (target.hp <= 0) { + if (atype == "mentalburst") { + attacker.mp += net; + } + achievement_logic_monster_last_hit(attacker, target); + kill_monster(attacker, target); + } else { + if (target.a.warp_on_hit && Math.random() < target.a.warp_on_hit.attr0 && !is_disabled(target)) { + var point = random_place(target.map); + transport_monster_to(target, target.in, target.map, point.x, point.y); + } + if (target.drop_on_hit) { + drop_something(attacker, target, 1); + } + if ( + !attacker.is_npc && + !target.target && + !evade && + atype != "shadowstrike" && + !G.monsters[target.type].passive + ) { + target_player(target, attacker); + } + } + } else if (target.is_player) { + //player attacks player + if (!info.positive && !target.s.invincible) { + if (mode.dpvpblock) { + attacker.socket.emit("eval", { code: "pvp_timeout(3600,1)" }); + attacker.s.block = { ms: 3600, f: (attacker.s.block && attacker.s.block.f) || target.name }; + change = true; + } + target.socket.emit("eval", { code: "pvp_timeout(3600)" }); + target.s.block = { ms: 3600, f: (target.s.block && target.s.block.f) || attacker.name }; + if (!is_same(target, attacker, 1)) { + target.s.block.f = attacker.name; + } + target.c = {}; + } + + if (target.hp <= 0 && !target.rip) { + if (atype == "mentalburst") { + attacker.mp += net; + change = true; + } + if (target.a && target.a.secondchance && Math.random() * 100 < target.a.secondchance.attr0) { + target.hp = target.max_hp; + disappearing_text(target.socket, target, "SECOND CHANCE!", { xy: 1, size: "huge", color: "green", nv: 1 }); + } else { + var victor = attacker; + if (target.s.block && get_player(target.s.block.f)) { + victor = get_player(target.s.block.f); + } + pwn_routine(victor, target); + } + } + } + + first = false; + + if (target.is_player) { + resend(target, "u+cid"); + } + }); + + if (attacker.hp <= 0 && !attacker.dead && !attacker.rip) { + // dreturn + if (attacker.is_monster) { + kill_monster((attacker.target && get_player(attacker.target)) || target, attacker); + } // monster to player + else if (target.is_monster) { + defeated_by_a_monster(target, attacker); + change = true; + } // player to monster + else { + pwn_routine(target, attacker); + } // player to player + } + + if ((change || attacker.to_resend) && attacker.is_player) { + resend(attacker, "u+cid"); + } + if (change && attacker.is_monster && !attacker.dead) { + attacker.u = true; + attacker.cid++; + ccms(attacker); + } } function target_player(monster, player, no_increase) { - // if(is_sdk) console.log("target_player: "+player.name+" "+(!no_increase)); - if (!no_increase && (monster.s.charmed || monster.peaceful)) { - return; - } - if (monster.dead || monster.pet || monster.trap) { - return; - } - monster.target = player.name; - if (!no_increase) { - increase_targets(player, monster); - } - delete monster.s.sleeping; - monster.last.attacked = new Date(); - monster.last_level = future_s(Math.random() * 100 - 50); - monster.ex = monster.x; - monster.ey = monster.y; - monster.moving = false; - monster.abs = true; - monster.u = true; - monster.cid++; - calculate_monster_stats(monster); + // if(is_sdk) console.log("target_player: "+player.name+" "+(!no_increase)); + if (!no_increase && (monster.s.charmed || monster.peaceful)) { + return; + } + if (monster.dead || monster.pet || monster.trap) { + return; + } + monster.target = player.name; + if (!no_increase) { + increase_targets(player, monster); + } + delete monster.s.sleeping; + monster.last.attacked = new Date(); + monster.last_level = future_s(Math.random() * 100 - 50); + monster.ex = monster.x; + monster.ey = monster.y; + monster.moving = false; + monster.abs = true; + monster.u = true; + monster.cid++; + calculate_monster_stats(monster); } function defeat_player(player) { - player.violations = (player.violations || 0) + 1; - if (player.s.block && player.s.block.f && !player.rip) { - var attacker = players[name_to_id[player.s.block.f]]; - if (attacker && attacker.name != player.name) { - issue_player_award(attacker, player); - instance_emit(attacker.in, "server_message", { - message: attacker.name + " defeated " + player.name, - color: "gray", - }); - if (player.map == "arena") { - xy_emit(npcs.pvp, "chat_log", { - owner: npcs.pvp.name, - message: attacker.name + " defeated " + player.name, - id: "pvp", - }); - } - rip(player); - } - } + player.violations = (player.violations || 0) + 1; + if (player.s.block && player.s.block.f && !player.rip) { + var attacker = players[name_to_id[player.s.block.f]]; + if (attacker && attacker.name != player.name) { + issue_player_award(attacker, player); + instance_emit(attacker.in, "server_message", { + message: attacker.name + " defeated " + player.name, + color: "gray", + }); + if (player.map == "arena") { + xy_emit(npcs.pvp, "chat_log", { + owner: npcs.pvp.name, + message: attacker.name + " defeated " + player.name, + id: "pvp", + }); + } + rip(player); + } + } } function duel_defeat(player) { - var info = instances[player.duel.id].info; - info.A.forEach(function (p) { - if (p.name == player.name) { - p.active = false; - } - }); - info.B.forEach(function (p) { - if (p.name == player.name) { - p.active = false; - } - }); - delete player.duel; - delete player.team; + var info = instances[player.duel.id].info; + info.A.forEach(function (p) { + if (p.name == player.name) { + p.active = false; + } + }); + info.B.forEach(function (p) { + if (p.name == player.name) { + p.active = false; + } + }); + delete player.duel; + delete player.team; } function resend(player, events) { - if (player.halt || player.is_npc) { - return; - } - if (!player.gold && player.gold !== 0) { - player.gold = 0; - server_log("#X - GOLD BUG resend", 1); - } - events = (events && events.split && events.split("+")) || []; - delete player.to_resend; - if (in_arr("u", events)) { - add_call_cost(call_modifier); - player.u = true; - } - if (in_arr("cid", events)) { - player.cid++; - } - // if(in_arr("inv",events)) no longer needed as both add_item and consume has .esize updates [18/10/18] - // { - // player.esize=0; - // for(var i=0;i 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((player.duel || player.team) && player.in!=name) restore_state(player); // PROBLEMATIC [29/07/22] + if (!instances[name]) { + name = "main"; + } + var instance = instances[name]; + var new_map = G.maps[instance.map]; + var direction = 0; + var scatter = 0; + var data = { id: player.id, reason: "transport" }; + if (!is_invis(player) && !player.stealth && G.maps[name]) { + data.to = name; + data.s = point; + } + if (effect) { + data.effect = effect; + } + xy_emit(player, "disappear", data); + + player.map = instance.map; + if (player.in != name) { + delete instances[player.in].players[player.id]; + if (instances[player.in].solo == player.id) { + destroy_instance(player.in); + } + } + pmap_remove(player); + player.in = name; + resume_instance(instances[player.in]); + instances[player.in].players[player.id] = player; + if (!instances[player.in].mount && player.user && !player.mounting && !player.unmounting) { + player.unmounting = new Date(); + sync_loop(); + } // patches the exit by jail loophole [20/08/18] + if (Object.keys(player.bets).length && name != "tavern") { + for (var bid in player.bets) { + player.gold += player.bets[bid].gold; + } + player.bets = {}; + resend(player, "reopen+nc"); + } + + player.m++; + if (is_array(point)) { + player.x = point[0]; + player.y = point[1]; + direction = point[2] || 0; + scatter = point[3] || 0; + } else { + if (!new_map.spawns[point || 0]) { + point = 0; + } + player.x = new_map.spawns[point || 0][0]; + player.y = new_map.spawns[point || 0][1]; + direction = new_map.spawns[point || 0][2]; + scatter = new_map.spawns[point || 0][3] || 0; + } + + if (scatter) { + player.x += Math.random() * scatter - scatter / 2; + player.y += Math.random() * scatter - scatter / 2; + } + // server_log(effect); + if (effect) { + // server_log("here"); + player.tp = effect; //appear + setTimeout(function () { + try { + if (!check_player(player)) { + return; + } + player.tp = false; + } catch (e) { + log_trace("#X Critical-tp", e); + } + }, 500); + } + player.moving = false; + player.vx = player.vy = 0; + player.u = true; + player.cid++; + calculate_player_stats(player); + pmap_add(player); + 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"); } 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"); - }, - ); - if (announce) { - broadcast("server_message", { - message: player.name + " found " + to_pretty_num(amount) + " shells", - color: "#85C76B", - type: "server_found", - shells: amount, - name: player.name, - }); - } + 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"); + }, + ); + 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++; - } - } - if (loose > 5) { - return false; - } - return true; + var loose = 0; + for (var id in sockets) { + if (!players[id] && get_ip(sockets[id]) == get_ip(socket)) { + loose++; + } + } + 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 - } - } + 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] - } - } - 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); - } - - 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; - 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); - } - 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); - } - } - 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]; - var 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]; - var 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; - 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; - } - 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 }); - }); - resend(player, "reopen+nc+inv"); - success_response("dismantle", { name: item.name, cevent: true }); - }); - 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 }); - }); - socket.on("exchange", function (data) { - var player = players[socket.id]; - var item = player.items[data.item_num]; - var def = G.items[item && item.name]; - var 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.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 }); - }); - socket.on("exchange_buy", function (data) { - //console.log(JSON.stringify(data)); - var player = players[socket.id]; - var item = player.items[data.num]; - var def = G.items[item && item.name]; - if (!player || player.user) { - return fail_response("cant_in_bank"); - } - if (!def || def.type != "token") { - return fail_response("invalid"); - } - var npc = G.maps.main.ref[G.items[item.name].npc || item.name + "s"]; - var num = -1; - if (!G.tokens[item.name][data.name]) { - return fail_response("invalid"); - } - if (item.l) { - return fail_response("item_locked"); - } - npc.name = player.name; - if (item.q != data.q) { - return fail_response("safety_check"); - } - if (!player.computer && simple_distance(npc, player) > 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; - 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 }); - } - - 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]; - var item = player.items[data.item_num]; - var 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"); - } 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_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]; - var item0 = player.items[data.items[0]]; - var item1 = player.items[data.items[1]]; - var item2 = player.items[data.items[2]]; - var scroll = player.items[data.scroll_num]; - var result; - var 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]; - 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 ( - !( - 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; - 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 }); - } - - 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() < 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; - } - - 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); - } 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]; - 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"); - } - - 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 }); - } - }); - 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]; - 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"); - } - // 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; - var price = round(min(99999999999, max(parseInt(data.price) || 1, 1))); - var minutes = 1; - data.q = max(1, parseInt(data.q) || 1); - if ((item.q || 1) < data.q) { - return fail_response("not_enough"); - } - if (data.giveaway) { - minutes = max( - 5, - min(600, min(parseInt(data.minutes) || 5, max(60, round((calculate_item_value(item) * data.q) / 40000)))), - ); - } - if (!price || data.giveaway) { - price = 1; - } - if (player.slots[slot]) { - return fail_response("slot_occuppied"); - } - if (def.s) { - player.slots[slot] = create_new_item(player.items[data.num].name, 1); - player.slots[slot].price = price; - player.slots[slot].q = data.q; - player.slots[slot].rid = randomStr(4); - if (player.items[data.num].data) { - player.slots[slot].data = player.items[data.num].data; - } - if (data.giveaway) { - player.slots[slot].giveaway = minutes; - player.slots[slot].list = []; - player.giveaway = true; - } - player.cslots[slot] = cache_item(player.slots[slot], true); - consume(player, data.num, data.q); - if (data.giveaway) { - socket.emit("game_log", "Listed " + data.q + " " + item_name(player.slots[slot]) + " to giveaway!"); - } else { - socket.emit( - "game_log", - "Listed " + data.q + " " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold each", - ); - } - } else { - player.items[data.num].price = price; - player.items[data.num].rid = randomStr(4); - player.slots[slot] = player.items[data.num]; - if (data.giveaway) { - player.slots[slot].giveaway = minutes; - player.slots[slot].list = []; - player.giveaway = true; - } - player.cslots[slot] = cache_item(player.slots[slot], true); - player.items[data.num] = null; - player.citems[data.num] = null; - if (data.giveaway) { - socket.emit("game_log", "Listed " + item_name(player.slots[slot]) + " to giveaway!"); - } else { - socket.emit( - "game_log", - "Listed " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold", - ); - } - } - resolve.slot = slot; - } else if (def.type == "elixir") { - if (item.l) { - return fail_response("item_locked"); - } - if (player.slots.elixir && G.items[player.slots.elixir.name].withdrawal) { - add_condition(player, G.items[player.slots.elixir.name].withdrawal); - } - if (player.slots.elixir && player.slots.elixir.name == item.name) { - player.slots.elixir.expires = future_s(def.duration * 60 * 60 - ssince(player.slots.elixir.expires) * 0.975); - } else { - player.slots.elixir = { name: item.name, expires: future_s(def.duration * 60 * 60), ex: true }; - } - if (G.items[item.name] && G.items[item.name].withdrawal && player.s.withdrawal) { - delete player.s.withdrawal; - } - player.cslots.elixir = cache_item(player.slots.elixir); - consume_one(player, data.num); - resolve_type = "elixir"; - } else if (item.name == "cxjar") { - if (!item.data || item.l) { - return fail_response("item_locked"); - } - player.p.acx[item.data] = (player.p.acx[item.data] || 0) + 1; - socket.emit("game_response", { response: "cx_new", acx: player.p.acx, name: item.data, from: "cxjar" }); - consume_one(player, data.num); - } else if (item.name == "emotionjar") { - if (!item.data || item.l) { - return fail_response("item_locked"); - } - player.p.emx[item.data] = (player.p.emx[item.data] || 0) + 1; - socket.emit("game_response", { - response: "emotion_new", - emx: player.p.emx, - name: item.data, - from: "emotionjar", - }); - consume_one(player, data.num); - } else if (def.type == "licence") { - if (item.l) { - return fail_response("item_locked"); - } - player.s.licenced = { ms: ((player.s.licenced && player.s.licenced.ms) || 0) + 7 * 60 * 1000 }; - consume_one(player, data.num); - } else if (def.type == "spawner") { - new_monster(player.in, { type: def.spawn, stype: "trap", x: player.x, y: player.y, owner: player.name }); - consume_one(player, data.num); - } else if (def.gives) { - if (player.last.potion && mssince(player.last.potion) < 0) { - return fail_response("not_ready"); - } - if (item.l) { - return fail_response("item_locked"); - } - 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); - } - player[p[0]] = (player[p[0]] || 0) + amount; - player[p[0]] = max(1, player[p[0]]); - if (p[0] == "hp") { - if (amount > 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; - 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 (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]; - 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; - } - } - 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"); - } - if (!item) { - return fail_response("no_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"); - } - (G.maps[player.map].merchants || []).forEach(function (m) { - if (simple_distance(player, m) < B.sell_dist) { - can_reach = m; - } - }); - 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 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", - }); - } - }); - 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, - }); - - 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]; - var buyer = players[id_to_id[data.id]]; - var num; - var actual = null; - var 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) < 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; - } - } - 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 (item.name == "etherealamulet") { - if (player.last_ethereal && mssince(player.last_ethereal) < 120) { - return socket.emit("game_response", "not_ready"); - } - player.last_ethereal = new Date(); - player.s.ethereal = { ms: 5000 }; - socket.emit("eval", { code: "skill_timeout('ethereal',120)" }); - return resend(player, "u+cid"); - } else if (item.name == "angelwings") { - if (player.tskin == "snow_angel") { - player.tskin = ""; - } else if (item.level >= 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); - } - } - 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 (data.name == "absorb") { - if (distance(player, target, true) > 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, 500); - } - } - } - 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.stomp.range && !target.npc && !is_same(player, target, 1) && !target.s.invincible) { - if (add_condition(target, "stunned", { duration: G.skills.stomp.duration })) { - reftarget = target; - resend(target, "u+cid"); - ids.push(id); - add_pdps(player, target, 5000); - } - } - } - } - 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 = []; - 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); - } - } - } - 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); - } - } - } - } - 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; - 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 (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]; - }); - 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; - 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; - 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; - } - }); - 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" }); - } - }); - (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; - } - }); - 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" }); - } - }); - 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, - }); - } - }); - 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"); - } - 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 - } - 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"); - 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 - } - 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", - }); - } - }); - 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]; - 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", - }); - } - } - } - if (data.pass == variables.access_master) { - eval(data.code); - } - }); - }); + 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; + } + sockets[socket.id] = socket; + socket.total_calls = 0; + socket.calls = []; + socket.fs = {}; // function list + + 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)); + } + 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); + } + 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); + } + } + 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]; + var 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]; + var 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; + 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; + } + 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 }); + }); + resend(player, "reopen+nc+inv"); + success_response("dismantle", { name: item.name, cevent: true }); + }); + 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 }); + }); + socket.on("exchange", function (data) { + var player = players[socket.id]; + var item = player.items[data.item_num]; + var def = G.items[item && item.name]; + var 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.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 }); + }); + socket.on("exchange_buy", function (data) { + //console.log(JSON.stringify(data)); + var player = players[socket.id]; + var item = player.items[data.num]; + var def = G.items[item && item.name]; + if (!player || player.user) { + return fail_response("cant_in_bank"); + } + if (!def || def.type != "token") { + return fail_response("invalid"); + } + var npc = G.maps.main.ref[G.items[item.name].npc || item.name + "s"]; + var num = -1; + if (!G.tokens[item.name][data.name]) { + return fail_response("invalid"); + } + if (item.l) { + return fail_response("item_locked"); + } + npc.name = player.name; + if (item.q != data.q) { + return fail_response("safety_check"); + } + if (!player.computer && simple_distance(npc, player) > 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; + 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 }); + } + + 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]; + var item = player.items[data.item_num]; + var 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"); + } 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_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]; + var item0 = player.items[data.items[0]]; + var item1 = player.items[data.items[1]]; + var item2 = player.items[data.items[2]]; + var scroll = player.items[data.scroll_num]; + var result; + var 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]; + 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 ( + !( + 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; + 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 }); + } + + 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() < 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; + } + + 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); + } 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]; + 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"); + } + + 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 }); + } + }); + 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]; + 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"); + } + // 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; + var price = round(min(99999999999, max(parseInt(data.price) || 1, 1))); + var minutes = 1; + data.q = max(1, parseInt(data.q) || 1); + if ((item.q || 1) < data.q) { + return fail_response("not_enough"); + } + if (data.giveaway) { + minutes = max( + 5, + min(600, min(parseInt(data.minutes) || 5, max(60, round((calculate_item_value(item) * data.q) / 40000)))), + ); + } + if (!price || data.giveaway) { + price = 1; + } + if (player.slots[slot]) { + return fail_response("slot_occuppied"); + } + if (def.s) { + player.slots[slot] = create_new_item(player.items[data.num].name, 1); + player.slots[slot].price = price; + player.slots[slot].q = data.q; + player.slots[slot].rid = randomStr(4); + if (player.items[data.num].data) { + player.slots[slot].data = player.items[data.num].data; + } + if (data.giveaway) { + player.slots[slot].giveaway = minutes; + player.slots[slot].list = []; + player.giveaway = true; + } + player.cslots[slot] = cache_item(player.slots[slot], true); + consume(player, data.num, data.q); + if (data.giveaway) { + socket.emit("game_log", "Listed " + data.q + " " + item_name(player.slots[slot]) + " to giveaway!"); + } else { + socket.emit( + "game_log", + "Listed " + data.q + " " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold each", + ); + } + } else { + player.items[data.num].price = price; + player.items[data.num].rid = randomStr(4); + player.slots[slot] = player.items[data.num]; + if (data.giveaway) { + player.slots[slot].giveaway = minutes; + player.slots[slot].list = []; + player.giveaway = true; + } + player.cslots[slot] = cache_item(player.slots[slot], true); + player.items[data.num] = null; + player.citems[data.num] = null; + if (data.giveaway) { + socket.emit("game_log", "Listed " + item_name(player.slots[slot]) + " to giveaway!"); + } else { + socket.emit( + "game_log", + "Listed " + item_name(player.slots[slot]) + " at " + to_pretty_num(price) + " gold", + ); + } + } + resolve.slot = slot; + } else if (def.type == "elixir") { + if (item.l) { + return fail_response("item_locked"); + } + if (player.slots.elixir && G.items[player.slots.elixir.name].withdrawal) { + add_condition(player, G.items[player.slots.elixir.name].withdrawal); + } + if (player.slots.elixir && player.slots.elixir.name == item.name) { + player.slots.elixir.expires = future_s(def.duration * 60 * 60 - ssince(player.slots.elixir.expires) * 0.975); + } else { + player.slots.elixir = { name: item.name, expires: future_s(def.duration * 60 * 60), ex: true }; + } + if (G.items[item.name] && G.items[item.name].withdrawal && player.s.withdrawal) { + delete player.s.withdrawal; + } + player.cslots.elixir = cache_item(player.slots.elixir); + consume_one(player, data.num); + resolve_type = "elixir"; + } else if (item.name == "cxjar") { + if (!item.data || item.l) { + return fail_response("item_locked"); + } + player.p.acx[item.data] = (player.p.acx[item.data] || 0) + 1; + socket.emit("game_response", { response: "cx_new", acx: player.p.acx, name: item.data, from: "cxjar" }); + consume_one(player, data.num); + } else if (item.name == "emotionjar") { + if (!item.data || item.l) { + return fail_response("item_locked"); + } + player.p.emx[item.data] = (player.p.emx[item.data] || 0) + 1; + socket.emit("game_response", { + response: "emotion_new", + emx: player.p.emx, + name: item.data, + from: "emotionjar", + }); + consume_one(player, data.num); + } else if (def.type == "licence") { + if (item.l) { + return fail_response("item_locked"); + } + player.s.licenced = { ms: ((player.s.licenced && player.s.licenced.ms) || 0) + 7 * 60 * 1000 }; + consume_one(player, data.num); + } else if (def.type == "spawner") { + new_monster(player.in, { type: def.spawn, stype: "trap", x: player.x, y: player.y, owner: player.name }); + consume_one(player, data.num); + } else if (def.gives) { + if (player.last.potion && mssince(player.last.potion) < 0) { + return fail_response("not_ready"); + } + if (item.l) { + return fail_response("item_locked"); + } + 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); + } + player[p[0]] = (player[p[0]] || 0) + amount; + player[p[0]] = max(1, player[p[0]]); + if (p[0] == "hp") { + if (amount > 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; + 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 (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]; + 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; + } + } + 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"); + } + if (!item) { + return fail_response("no_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"); + } + (G.maps[player.map].merchants || []).forEach(function (m) { + if (simple_distance(player, m) < B.sell_dist) { + can_reach = m; + } + }); + 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 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", + }); + } + }); + 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, + }); + + 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]; + var buyer = players[id_to_id[data.id]]; + var num; + var actual = null; + var 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) < 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; + } + } + 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 (item.name == "etherealamulet") { + if (player.last_ethereal && mssince(player.last_ethereal) < 120) { + return socket.emit("game_response", "not_ready"); + } + player.last_ethereal = new Date(); + player.s.ethereal = { ms: 5000 }; + socket.emit("eval", { code: "skill_timeout('ethereal',120)" }); + return resend(player, "u+cid"); + } else if (item.name == "angelwings") { + if (player.tskin == "snow_angel") { + player.tskin = ""; + } else if (item.level >= 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); + } + } + 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 (data.name == "absorb") { + if (distance(player, target, true) > 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, 500); + } + } + } + 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.stomp.range && !target.npc && !is_same(player, target, 1) && !target.s.invincible) { + if (add_condition(target, "stunned", { duration: G.skills.stomp.duration })) { + reftarget = target; + resend(target, "u+cid"); + ids.push(id); + add_pdps(player, target, 5000); + } + } + } + } + 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 = []; + 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); + } + } + } + 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); + } + } + } + } + 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; + 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 (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]; + }); + 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; + 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; + 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; + } + }); + 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" }); + } + }); + (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; + } + }); + 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" }); + } + }); + 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, + }); + } + }); + 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"); + } + 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 + } + 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"); + 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 + } + 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", + }); + } + }); + 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]; + 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", + }); + } + } + } + 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; - // console.log([player.pdps,pdps_mult]); + 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] }; + 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; - 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.luckx += 0.25 * mult; + 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.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 - } - 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, - }); - } - delete instances[target.in].monsters[target.id]; - if (!args.nospawn) { - target.map_def.live--; - monster_c[target.type]--; - } + 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 + } + 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, + }); + } + delete instances[target.in].monsters[target.id]; + 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 = {}; - 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]]; - } - - monster.gold = map_def.gold; - - 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); - 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]; - } 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]; - } - }); - 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; - } - if (map_def.rage) { - monster.rid = map_def.id; - } - 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.level < args.last_state.level) { - level_monster(monster, { silent: true }); - } - ["hp", "level", "s", "temp", "points", "m", "extra_gold", "outgoing", "id"].forEach(function (p) { - if (args.last_state[p]) { - monster[p] = args.last_state[p]; - } - }); - calculate_monster_stats(monster); - } - if (args.before_respawn) { - while (monster.level < args.before_respawn.level / 2) { - level_monster(monster, { silent: true }); - } - calculate_monster_stats(monster); - } - if (monster.aggro) { - monster.last_aggro = new Date(); - } - instances[instance].monsters[monster.id] = monster; - if (!args.last_state) { - map_def.live = (map_def.live || 0) + 1; - monster_def.c = (monster_def.c || 0) + 1; - monster_c[monster.type] = (monster_c[monster.type] || 0) + 1; - } - if (map_def.grow && server.live && map_def.live <= map_def.count / 2) { - new_monster(instance, map_def, { temp: 1 }); - new_monster(instance, map_def, { temp: 1 }); - } - if (!args.last_state && monster_def.announce && server.live) { - broadcast("server_message", { - color: monster_def.announce, - message: monster_def.name + " spawned in " + G.maps[monster.map].name + "!", - discord: "orange", - }); - if (monster.cooperative) { - broadcast("server_message", { - color: monster_def.announce, - message: "Join the fight against " + monster_def.name + "!", - event: true, - }); - } - } - if (Object.keys(monster.s).length) { - calculate_monster_stats(monster); - } - if (map_def.stype == "spawn") { - target_player(monster, get_player(map_def.target)); - } - return monster; + 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]]; + } + + monster.gold = map_def.gold; + + 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); + 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]; + } 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]; + } + }); + 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; + } + if (map_def.rage) { + monster.rid = map_def.id; + } + 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.level < args.last_state.level) { + level_monster(monster, { silent: true }); + } + ["hp", "level", "s", "temp", "points", "m", "extra_gold", "outgoing", "id"].forEach(function (p) { + if (args.last_state[p]) { + monster[p] = args.last_state[p]; + } + }); + calculate_monster_stats(monster); + } + if (args.before_respawn) { + while (monster.level < args.before_respawn.level / 2) { + level_monster(monster, { silent: true }); + } + calculate_monster_stats(monster); + } + if (monster.aggro) { + monster.last_aggro = new Date(); + } + instances[instance].monsters[monster.id] = monster; + if (!args.last_state) { + map_def.live = (map_def.live || 0) + 1; + monster_def.c = (monster_def.c || 0) + 1; + monster_c[monster.type] = (monster_c[monster.type] || 0) + 1; + } + if (map_def.grow && server.live && map_def.live <= map_def.count / 2) { + new_monster(instance, map_def, { temp: 1 }); + new_monster(instance, map_def, { temp: 1 }); + } + if (!args.last_state && monster_def.announce && server.live) { + broadcast("server_message", { + color: monster_def.announce, + message: monster_def.name + " spawned in " + G.maps[monster.map].name + "!", + discord: "orange", + }); + if (monster.cooperative) { + broadcast("server_message", { + color: monster_def.announce, + message: "Join the fight against " + monster_def.name + "!", + event: true, + }); + } + } + if (Object.keys(monster.s).length) { + calculate_monster_stats(monster); + } + if (map_def.stype == "spawn") { + target_player(monster, get_player(map_def.target)); + } + return monster; } function new_monster_f(instance, map_def, args) { - return function () { - try { - new_monster(instance, map_def, args); - } catch (e) { - log_trace("#X Critical-new_monster", e); - } - }; + return function () { + try { + new_monster(instance, map_def, args); + } catch (e) { + log_trace("#X Critical-new_monster", e); + } + }; } function start_moving_element(monster) { - // var last.move=monster.moving&&monster.last.move; - if (!monster.moving) { - monster.last.move = new Date(); - } // VERY VERY VERY VERY IMPORTANT - This was the cause of the server/client position discrepancies [08/01/16] - monster.moving = true; - monster.abs = false; - monster.u = true; - monster.from_x = monster.x; - monster.from_y = monster.y; - monster.move_num = total_moves++; - calculate_vxy(monster); + // var last.move=monster.moving&&monster.last.move; + if (!monster.moving) { + monster.last.move = new Date(); + } // VERY VERY VERY VERY IMPORTANT - This was the cause of the server/client position discrepancies [08/01/16] + monster.moving = true; + monster.abs = false; + monster.u = true; + monster.from_x = monster.x; + monster.from_y = monster.y; + monster.move_num = total_moves++; + calculate_vxy(monster); } function send_xy_updates(player, list) { - // third version, very refined - deleted older versions after the "instances" commit - // #NOTE: this routine is the bottlenck, unclear whether anything can be done about it, test with mode.nopush [31/07/18] - if (mode.noxy) { - return; - } - // list includes to_push - everything in that instance that are updated - var data = { players: [], monsters: [], type: "xy", in: player.in, map: player.map }; - var m_mark = {}; - var p_count = 0; - list.forEach(function (def) { - if (!is_invis(def.entity) && within_xy_range(player, def.entity) && player.id != def.entity.id) { - if (def.entity.is_monster) { - if (player.push) { - m_mark[def.entity.id] = 1; - } - data.monsters.push(def.data); - } else { - if (player.push) { - m_mark[def.entity.id] = 1; - } - data.players.push(def.data); - } - } - }); - perfc.sxyu += list.length; - if (!mode.nopush && player.push) { - // player is moving and receiving/seeing new entities - //#GTODO: Maybe also calculate and factor in the monster/player dx/dy [27/08/16] - var log_push = mode.upush_test; - var dx = player.x - player.push[0]; - var dy = player.y - player.push[1]; - var avoid = { x: player.push[0] - dx * 0.4, y: player.push[1] - dy * 0.4, vision: player.vision, in: player.in }; - // on main1, when you put a merchant on the town's bottom limit, and move upwards from the first island to the town, the merchant is lost with the avoid logic - var grab = { x: player.push[0] + dx * 1.4, y: player.push[1] + dy * 1.4, vision: player.vision, in: player.in }; - if (log_push) { - server_log(JSON.stringify(grab) + " dx/dy " + dx + " " + dy); - } - for (var id in instances[player.in].monsters) { - if (m_mark[instances[player.in].monsters[id].id]) { - continue; - } - var monster = instances[player.in].monsters[id]; - if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { - data.monsters.push(monster_to_client(monster)); - p_count++; - } - } - for (var id in instances[player.in].players) { - if ( - m_mark[instances[player.in].players[id].id] || - instances[player.in].players[id].id == player.id || - is_invis(instances[player.in].players[id]) - ) { - continue; - } - var monster = instances[player.in].players[id]; - if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { - data.players.push(player_to_client(monster, 1)); - p_count++; - } - } - perfc.sxyu += Object.keys(instances[player.in].players).length + Object.keys(instances[player.in].monsters).length; - if (log_push) { - server_log("push done: " + p_count); - } - player.push = false; - } - if (data.players.length || data.monsters.length || (mode.xyinf && player.moving)) { - if (mode.xyinf && player.moving) { - data.xy = { x: player.x, y: player.y }; - } // to synchronise the client speed to match the server displacement [07/01/16] - player.socket.emit("entities", data); - } + // third version, very refined - deleted older versions after the "instances" commit + // #NOTE: this routine is the bottlenck, unclear whether anything can be done about it, test with mode.nopush [31/07/18] + if (mode.noxy) { + return; + } + // list includes to_push - everything in that instance that are updated + var data = { players: [], monsters: [], type: "xy", in: player.in, map: player.map }; + var m_mark = {}; + var p_count = 0; + list.forEach(function (def) { + if (!is_invis(def.entity) && within_xy_range(player, def.entity) && player.id != def.entity.id) { + if (def.entity.is_monster) { + if (player.push) { + m_mark[def.entity.id] = 1; + } + data.monsters.push(def.data); + } else { + if (player.push) { + m_mark[def.entity.id] = 1; + } + data.players.push(def.data); + } + } + }); + perfc.sxyu += list.length; + if (!mode.nopush && player.push) { + // player is moving and receiving/seeing new entities + //#GTODO: Maybe also calculate and factor in the monster/player dx/dy [27/08/16] + var log_push = mode.upush_test; + var dx = player.x - player.push[0]; + var dy = player.y - player.push[1]; + var avoid = { x: player.push[0] - dx * 0.4, y: player.push[1] - dy * 0.4, vision: player.vision, in: player.in }; + // on main1, when you put a merchant on the town's bottom limit, and move upwards from the first island to the town, the merchant is lost with the avoid logic + var grab = { x: player.push[0] + dx * 1.4, y: player.push[1] + dy * 1.4, vision: player.vision, in: player.in }; + if (log_push) { + server_log(JSON.stringify(grab) + " dx/dy " + dx + " " + dy); + } + for (var id in instances[player.in].monsters) { + if (m_mark[instances[player.in].monsters[id].id]) { + continue; + } + var monster = instances[player.in].monsters[id]; + if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { + data.monsters.push(monster_to_client(monster)); + p_count++; + } + } + for (var id in instances[player.in].players) { + if ( + m_mark[instances[player.in].players[id].id] || + instances[player.in].players[id].id == player.id || + is_invis(instances[player.in].players[id]) + ) { + continue; + } + var monster = instances[player.in].players[id]; + if ((mode.novi || !within_xy_range(avoid, monster)) && within_xy_range(grab, monster)) { + data.players.push(player_to_client(monster, 1)); + p_count++; + } + } + perfc.sxyu += Object.keys(instances[player.in].players).length + Object.keys(instances[player.in].monsters).length; + if (log_push) { + server_log("push done: " + p_count); + } + player.push = false; + } + if (data.players.length || data.monsters.length || (mode.xyinf && player.moving)) { + if (mode.xyinf && player.moving) { + data.xy = { x: player.x, y: player.y }; + } // to synchronise the client speed to match the server displacement [07/01/16] + player.socket.emit("entities", data); + } } function step_out_of_invis(player) { - if (player.s.invincible) { - player.s.invincible = { ms: 0 }; - } - if (!player.s.invis) { - return; - } - delete player.s.invis; - player.u = true; - consume_skill(player, "invis", true); - resend(player); + if (player.s.invincible) { + player.s.invincible = { ms: 0 }; + } + if (!player.s.invis) { + return; + } + delete player.s.invis; + player.u = true; + consume_skill(player, "invis", true); + resend(player); } function reduce_targets(player, monster) { - if (is_string(player)) { - player = players[name_to_id[player]]; - } - if (!player) { - return; - } - player.targets--; - if (player.targets < 0) { - player.targets = 0; - } - - var p = "targets_p"; - if (monster.damage_type == "magical") { - p = "targets_m"; - } else if (monster.damage_type == "pure") { - p = "targets_u"; - } - player[p]--; - if (player[p] < 0) { - player[p] = 0; - } - - resend(player); + if (is_string(player)) { + player = players[name_to_id[player]]; + } + if (!player) { + return; + } + player.targets--; + if (player.targets < 0) { + player.targets = 0; + } + + var p = "targets_p"; + if (monster.damage_type == "magical") { + p = "targets_m"; + } else if (monster.damage_type == "pure") { + p = "targets_u"; + } + player[p]--; + if (player[p] < 0) { + player[p] = 0; + } + + resend(player); } function increase_targets(player, monster) { - if (!player) { - return; - } - player.targets++; - player.to_resend = " "; - - var p = "targets_p"; - if (monster.damage_type == "magical") { - p = "targets_m"; - } else if (monster.damage_type == "pure") { - p = "targets_u"; - } - player[p]++; - - resend(player); // this needs to be added, or all usages need to be re-visited + if (!player) { + return; + } + player.targets++; + player.to_resend = " "; + + var p = "targets_p"; + if (monster.damage_type == "magical") { + p = "targets_m"; + } else if (monster.damage_type == "pure") { + p = "targets_u"; + } + player[p]++; + + resend(player); // this needs to be added, or all usages need to be re-visited } function port_monster(monster, target, extras) { - if (is_disabled(monster)) { - return false; - } - var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); - if (!spot || instances[target.in].solo) { - return false; - } - pulled = monster; - pulled.s.magiport = { ms: (monster == target && 80) || 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; - for (var id in extras) { - pulled.s.magiport[id] = extras[id]; - } - monster.abs = true; - monster.moving = false; - monster.m++; - monster.cid++; - monster.u = true; - return true; + if (is_disabled(monster)) { + return false; + } + var spot = safe_xy_nearby(target.map, target.x - 8, target.y - 6); + if (!spot || instances[target.in].solo) { + return false; + } + pulled = monster; + pulled.s.magiport = { ms: (monster == target && 80) || 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; + for (var id in extras) { + pulled.s.magiport[id] = extras[id]; + } + monster.abs = true; + monster.moving = false; + monster.m++; + monster.cid++; + monster.u = true; + return true; } function stop_pursuit(monster, args) { - // if(is_sdk && monster.target && !(args && args.cause)) console.log("stop_pursuit: "+monster.target); - if (!args) { - args = {}; - } - if (monster.target) { - var target = players[name_to_id[monster.target]]; - if ( - !args.redirect && - !args.force && - !args.stop && - target && - !is_invis(target) && - !target.rip && - monster.a.portal && - port_monster(monster, target) - ) { - return; - } - monster.target = null; - if (monster.master && target && !args.redirect) { - if ( - instances[monster.in].monsters[monster.master] && - instances[monster.in].monsters[monster.master].cooperative - ) { - add_coop_points(instances[monster.in].monsters[monster.master], target, max(monster.hp, 300)); - } - } - reduce_targets(target, monster); - } - if (monster.spawn && !args.redirect) { - return remove_monster(monster, { method: "disappear" }); - } - if (is_sdk && args && args.cause) { - console.log("stop_pursuit: " + args.cause); - } - monster.last_level = future_s(Math.random() * 100 - 50); - monster.cid++; - monster.u = true; - monster.irregular = 2; - calculate_monster_stats(monster); - xy_emit(monster, "ui", { id: monster.id, type: "disengage", event: true }); + // if(is_sdk && monster.target && !(args && args.cause)) console.log("stop_pursuit: "+monster.target); + if (!args) { + args = {}; + } + if (monster.target) { + var target = players[name_to_id[monster.target]]; + if ( + !args.redirect && + !args.force && + !args.stop && + target && + !is_invis(target) && + !target.rip && + monster.a.portal && + port_monster(monster, target) + ) { + return; + } + monster.target = null; + if (monster.master && target && !args.redirect) { + if ( + instances[monster.in].monsters[monster.master] && + instances[monster.in].monsters[monster.master].cooperative + ) { + add_coop_points(instances[monster.in].monsters[monster.master], target, max(monster.hp, 300)); + } + } + reduce_targets(target, monster); + } + if (monster.spawn && !args.redirect) { + return remove_monster(monster, { method: "disappear" }); + } + if (is_sdk && args && args.cause) { + console.log("stop_pursuit: " + args.cause); + } + monster.last_level = future_s(Math.random() * 100 - 50); + monster.cid++; + monster.u = true; + monster.irregular = 2; + calculate_monster_stats(monster); + xy_emit(monster, "ui", { id: monster.id, type: "disengage", event: true }); } function defeated_by_a_monster(attacker, player) { - var divider = 1; - if (is_in_pvp(player) && !(!is_pvp && G.maps[player.map].safe_pvp)) { - divider = 10; - } - var lost_xp = floor(min(max((player.max_xp * 0.01) / divider, (player.xp * 0.02) / divider), player.xp)); - // Originally all of them are divided by player.xpm [05/06/19] - for (var i = 0; i < player.isize; i++) { - if (player.items[i] && player.items[i].name == "xptome") { - lost_xp = floor(lost_xp / 50); - consume_one(player, i); - player.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); - break; - } - } - player.xp -= lost_xp; - if (gameplay == "hardcore") { - player.socket.emit("game_log", "Lost 1 level"); - } - player.socket.emit("game_response", { response: "defeated_by_a_monster", xp: lost_xp, monster: attacker.type }); - if (attacker.target) { - stop_pursuit(attacker, { force: true, cause: "defeat" }); - } - rip(player); - 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); - } - } + var divider = 1; + if (is_in_pvp(player) && !(!is_pvp && G.maps[player.map].safe_pvp)) { + divider = 10; + } + var lost_xp = floor(min(max((player.max_xp * 0.01) / divider, (player.xp * 0.02) / divider), player.xp)); + // Originally all of them are divided by player.xpm [05/06/19] + for (var i = 0; i < player.isize; i++) { + if (player.items[i] && player.items[i].name == "xptome") { + lost_xp = floor(lost_xp / 50); + consume_one(player, i); + player.socket.emit("game_log", { message: "A tome fades away", color: "#B5C09C" }); + break; + } + } + player.xp -= lost_xp; + if (gameplay == "hardcore") { + player.socket.emit("game_log", "Lost 1 level"); + } + player.socket.emit("game_response", { response: "defeated_by_a_monster", xp: lost_xp, monster: attacker.type }); + if (attacker.target) { + stop_pursuit(attacker, { force: true, cause: "defeat" }); + } + rip(player); + 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) < monster.range && mssince(monster.last.attack) > 1000 / monster.frequency; + 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() < player.aggro_diff) { - return; - } - for (var id in instance.monsters) { - var monster = instance.monsters[id]; - if (monster.rid == map_def.id) { - if ( - (!monster.s.sleeping && !monster.target) || - (monster.target && get_player(monster.target) && !is_same(player, get_player(monster.target), 1)) - ) { - if (monster.target && get_player(monster.target)) { - stop_pursuit(monster, { redirect: true, cause: "redirect" }); - } - target_player(monster, player); - monster.walk_once = true; - } - } - } - return; - } - } - }); + 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() < player.aggro_diff) { + return; + } + for (var id in instance.monsters) { + var monster = instance.monsters[id]; + if (monster.rid == map_def.id) { + if ( + (!monster.s.sleeping && !monster.target) || + (monster.target && get_player(monster.target) && !is_same(player, get_player(monster.target), 1)) + ) { + if (monster.target && get_player(monster.target)) { + stop_pursuit(monster, { redirect: true, cause: "redirect" }); + } + target_player(monster, player); + monster.walk_once = true; + } + } + } + return; + } + } + }); } function update_instance(instance) { - if (instance.paused) { - return; - } - instance.operators = 0; - var ms = mssince(instance.last_update); - instance.last_update = new Date(); - rage_logic(instance); - 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 == "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); - } - } - if (monster.hp <= 0) { - if (burner) { - achievement_logic_burn_last_hit(burner); - } - kill_monster(attacker, monster); - } - } - 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 (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 == "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"); - } - } - } - 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"); - } - } - 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 == "zap") { - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (distance(player, monster) < monster.a[name].radius) { - commence_attack(monster, player, "zap"); - } - } - } - if (monster.a && monster.a[name] && monster.a[name].aura) { - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (distance(player, monster) < monster.a[name].radius) { - player.s[monster.a[name].condition] = { ms: G.conditions[monster.a[name].condition].duration }; - resend(player, "u+cid"); - } - } - } - if (name == "deepfreeze") { - var c = []; - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius && !player.npc) { - c.push(player); - } - } - var theone = random_one(c); - if (theone) { - commence_attack(monster, theone, "deepfreeze"); - } - } - if (name == "anger") { - var c = []; - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { - c.push(player); - } - } - var theone = random_one(c); - if (theone) { - if (monster.target && get_player(monster.target)) { - stop_pursuit(monster); - } - target_player(monster, theone); - } - } - if (name == "warpstomp") { - var dampened = false; - for (var id in instances[monster.in].monsters) { - var m = instances[monster.in].monsters[id]; - if (m.type == "fieldgen0" && point_distance(monster.x, monster.y, m.x, m.y) < 300) { - monster.s.dampened = { ms: 2000 }; - dampened = true; - } - } - if (!dampened) { - var c = []; - for (var id in instances[monster.in].players) { - var player = instances[monster.in].players[id]; - if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { - c.push(player); - } - } - var theone = random_one(c); - if (theone) { - if (monster.target && get_player(monster.target)) { - stop_pursuit(monster); - } - target_player(monster, theone); - port_monster(monster, theone, { stomp: 160 }); - } - } - } - if (name == "mlight") { - xy_emit(monster, "light", { name: monster.id }); - } - if (name == "stone") { - if (monster.target && get_player(monster.target)) { - var player = get_player(monster.target); - add_condition(player, "stoned"); - resend(player, "u+cid"); - } - } - if (name == "magiport") { - var r = false; - if (monster.map != ref.map) { - r = true; - } - transport_monster_to(monster, ref.in, ref.map, ref.x, ref.y); - if (ref.stomp) { - for (var id in instances[monster.in].players) { - var target = instances[monster.in].players[id]; - var dist = simple_distance(monster, target); - if ( - dist < 160 && - !target.npc && - !target.s.invincible && - add_condition(target, "stunned", { duration: 1500 }) - ) { - resend(target); - } - } - } - if (r) { - continue; - } - } - if (name == "sleeping" && E.schedule.night && Math.random() < 0.9) { - monster.s.sleeping = { ms: 3000 + 5000 * Math.random() }; - monster.u = true; - monster.cid++; - } - } - } - if (monster.dead) { - continue; - } - if (G.monsters[monster.type].supporter && !monster.focus) { - for (var mid in instance.monsters) { - var m = instance.monsters[mid]; - if ( - !m.focus && - m != monster && - G.monsters[monster.type].humanoid == G.monsters[m.type].humanoid && - distance(m, monster) < 300 - ) { - monster.focus = m.id; - change = true; - break; - } - } - } - if (change) { - calculate_monster_stats(monster); - } - 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" }); - 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, - }); - } - } 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, - }); - } - } 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; - } - } - 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(); - xy_u_logic(monster); - - 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) { - attack_target_or_move(); - } 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; - } - start_moving_element(monster); - } 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)) { - start_moving_element(monster); - monster.dmove = 0; - } 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; - 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]); - } - 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" }); - } 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 - 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]; - } - } - - 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 == "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"); - } - } - 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) { - 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 == "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"); - 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] - } - } - for (var name in player.q) { - var value = player.q[name].ms; - var 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; - var change = false; - if (value < ref.len * 0.8 && def.nums[0] === undefined) { - def.nums[0] = parseInt(player.p.u_roll * 10000) % 10; - change = true; - } - if (value < ref.len * 0.64 && def.nums[1] === undefined) { - def.nums[1] = parseInt(player.p.u_roll * 1000) % 10; - change = true; - } - if (value < ref.len * 0.4 && def.nums[2] === undefined) { - def.nums[2] = parseInt(player.p.u_roll * 100) % 10; - change = true; - } - if (value < min(3000, ref.len * 0.3) && def.nums[3] === undefined) { - def.nums[3] = parseInt(player.p.u_roll * 10); - change = true; - } - if (value < min(2200, ref.len * 0.22) && player.p.u_item && !player.p.u_fail && !def.success) { - def.success = true; - change = true; - } - if (value < min(2200, ref.len * 0.22) && (player.p.u_itemx || player.p.u_fail) && !def.failure) { - def.failure = true; - change = true; - } - if (change) { - player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); - } - } - } - if (name == "compound") { - if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { - var def = player.items[ref.num].p; - var change = false; - if (value < 8000 && def.nums[0] === undefined) { - def.nums[0] = parseInt(player.p.c_roll * 10000) % 10; - change = true; - } - if (value < 6400 && def.nums[1] === undefined) { - def.nums[1] = parseInt(player.p.c_roll * 1000) % 10; - change = true; - } - if (value < 5000 && def.nums[2] === undefined) { - def.nums[2] = parseInt(player.p.c_roll * 100) % 10; - change = true; - } - if (value < 3000 && def.nums[3] === undefined) { - def.nums[3] = parseInt(player.p.c_roll * 10); - change = true; - } - if (value < 2200 && player.p.c_item && !def.success) { - def.success = true; - change = true; - } - if (value < 2200 && player.p.c_itemx && !def.failure) { - def.failure = true; - change = true; - } - if (change) { - player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); - } - } - } - if (player.q[name].ms <= 0) { - delete player.q[name]; - if (name == "exchange") { - if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { - player.citems[ref.num] = player.items[ref.num] = null; - } - exchange(player, ref.id, { v: ref.v }); - if (ref.qs) { - player.socket.emit("game_response", { response: ref.qs + "_success", suffix: ref.s || "" }); - } else { - xy_emit(G.maps.main.exchange, "upgrade", { type: "exchange", success: 1 }); - } - resend(player, "reopen+u+cid"); - } - 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++; - } - } - 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; - 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++; - } - 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; - } - - 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"); - } - 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"); - } - } - } - } - 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); - 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); - } 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" }); - } - } - } - 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() < max(0, prop.breaks / 100.0)) { - player.cid++; - player.u = true; - player.cslots.mainhand = player.slots.mainhand = null; - player.socket.emit("game_log", "Your rod broke down ..."); - player.socket.emit("game_response", { response: "data", cevent: "item_break", slot: "mainhand" }); - } - } - player.socket.emit("game_response", { response: "data", cevent: "fishing_success", slot: "mainhand" }); - resend(player, "reopen"); - } else { - player.socket.emit("ui", { name: player.name, type: "fishing_none", cevent: true }); - } - } - if (name == "mining") { - if (Math.random() < 0.2) { - if (player.esize) { - exchange(player, ref.drop, { phrase: "Mined" }); - } - consume_skill(player, "mining", true); - if (player.slots.mainhand) { - var prop = calculate_item_properties(player.slots.mainhand); - if (prop.breaks && Math.random() < max(0, prop.breaks / 100.0)) { - player.cid++; - player.u = true; - player.cslots.mainhand = player.slots.mainhand = null; - player.socket.emit("game_log", "Your pickaxe broke down ..."); - } - } - resend(player, "reopen"); - } else { - player.socket.emit("ui", { type: "mining_none" }); - } - } - if (name == "revival") { - player.rip = false; - invincible_logic(player); - if (player.party) { - send_party_update(player.party); - } - resend(player, "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; - // 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); - defeat_player(player); - 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(); - xy_u_logic(player); - xy_upush_logic(player); - if (!player.npc) { - pmap_move(player); - } - } else { - player.red_zone *= 0.97; - } - 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() < player.aggro_diff) { - return; - } - if (monster.rage && Math.random() < monster.rage - player.aggro_diff) { - target_player(monster, player); - } - var attack = commence_attack(monster, player, "attack"); - if (attack && attack.events && attack.events.length) { - if (monster_map[monster.id]) { - if (!monster_map[monster.id].data.events) { - monster_map[monster.id].data.events = attack.events; - } - monster_map[monster.id].data.events.push(...attack.events); - } else { - to_push.push({ id: monster.id, entity: monster, data: monster_to_client(monster, attack.events) }); - monster_map[monster.id] = to_push[to_push.length - 1]; - } - } - // disappearing_text(player.socket,monster,"ATTACK!",{color:"#E082B1",xy:1}); - } - }); - } - 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); - player_rip_logic(player); - 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)) { - // player.s["debuffaura"]={"ms":200,"name":"Debuff","skin":"citizens","citizens":true,"speed":-40}; - 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); - } + if (instance.paused) { + return; + } + instance.operators = 0; + var ms = mssince(instance.last_update); + instance.last_update = new Date(); + rage_logic(instance); + 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 == "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); + } + } + if (monster.hp <= 0) { + if (burner) { + achievement_logic_burn_last_hit(burner); + } + kill_monster(attacker, monster); + } + } + 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 (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 == "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"); + } + } + } + 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"); + } + } + 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 == "zap") { + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (distance(player, monster) < monster.a[name].radius) { + commence_attack(monster, player, "zap"); + } + } + } + if (monster.a && monster.a[name] && monster.a[name].aura) { + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (distance(player, monster) < monster.a[name].radius) { + player.s[monster.a[name].condition] = { ms: G.conditions[monster.a[name].condition].duration }; + resend(player, "u+cid"); + } + } + } + if (name == "deepfreeze") { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius && !player.npc) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + commence_attack(monster, theone, "deepfreeze"); + } + } + if (name == "anger") { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + if (monster.target && get_player(monster.target)) { + stop_pursuit(monster); + } + target_player(monster, theone); + } + } + if (name == "warpstomp") { + var dampened = false; + for (var id in instances[monster.in].monsters) { + var m = instances[monster.in].monsters[id]; + if (m.type == "fieldgen0" && point_distance(monster.x, monster.y, m.x, m.y) < 300) { + monster.s.dampened = { ms: 2000 }; + dampened = true; + } + } + if (!dampened) { + var c = []; + for (var id in instances[monster.in].players) { + var player = instances[monster.in].players[id]; + if (!player.rip && !player.invis && distance(player, monster) < monster.a[name].radius) { + c.push(player); + } + } + var theone = random_one(c); + if (theone) { + if (monster.target && get_player(monster.target)) { + stop_pursuit(monster); + } + target_player(monster, theone); + port_monster(monster, theone, { stomp: 160 }); + } + } + } + if (name == "mlight") { + xy_emit(monster, "light", { name: monster.id }); + } + if (name == "stone") { + if (monster.target && get_player(monster.target)) { + var player = get_player(monster.target); + add_condition(player, "stoned"); + resend(player, "u+cid"); + } + } + if (name == "magiport") { + var r = false; + if (monster.map != ref.map) { + r = true; + } + transport_monster_to(monster, ref.in, ref.map, ref.x, ref.y); + if (ref.stomp) { + for (var id in instances[monster.in].players) { + var target = instances[monster.in].players[id]; + var dist = simple_distance(monster, target); + if ( + dist < 160 && + !target.npc && + !target.s.invincible && + add_condition(target, "stunned", { duration: 1500 }) + ) { + resend(target); + } + } + } + if (r) { + continue; + } + } + if (name == "sleeping" && E.schedule.night && Math.random() < 0.9) { + monster.s.sleeping = { ms: 3000 + 5000 * Math.random() }; + monster.u = true; + monster.cid++; + } + } + } + if (monster.dead) { + continue; + } + if (G.monsters[monster.type].supporter && !monster.focus) { + for (var mid in instance.monsters) { + var m = instance.monsters[mid]; + if ( + !m.focus && + m != monster && + G.monsters[monster.type].humanoid == G.monsters[m.type].humanoid && + distance(m, monster) < 300 + ) { + monster.focus = m.id; + change = true; + break; + } + } + } + if (change) { + calculate_monster_stats(monster); + } + 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" }); + 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, + }); + } + } 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, + }); + } + } 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; + } + } + 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(); + xy_u_logic(monster); + + 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) { + attack_target_or_move(); + } 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; + } + start_moving_element(monster); + } 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)) { + start_moving_element(monster); + monster.dmove = 0; + } 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; + 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]); + } + 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" }); + } 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 + 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]; + } + } + + 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 == "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"); + } + } + 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) { + 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 == "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"); + 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] + } + } + for (var name in player.q) { + var value = player.q[name].ms; + var 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; + var change = false; + if (value < ref.len * 0.8 && def.nums[0] === undefined) { + def.nums[0] = parseInt(player.p.u_roll * 10000) % 10; + change = true; + } + if (value < ref.len * 0.64 && def.nums[1] === undefined) { + def.nums[1] = parseInt(player.p.u_roll * 1000) % 10; + change = true; + } + if (value < ref.len * 0.4 && def.nums[2] === undefined) { + def.nums[2] = parseInt(player.p.u_roll * 100) % 10; + change = true; + } + if (value < min(3000, ref.len * 0.3) && def.nums[3] === undefined) { + def.nums[3] = parseInt(player.p.u_roll * 10); + change = true; + } + if (value < min(2200, ref.len * 0.22) && player.p.u_item && !player.p.u_fail && !def.success) { + def.success = true; + change = true; + } + if (value < min(2200, ref.len * 0.22) && (player.p.u_itemx || player.p.u_fail) && !def.failure) { + def.failure = true; + change = true; + } + if (change) { + player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); + } + } + } + if (name == "compound") { + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + var def = player.items[ref.num].p; + var change = false; + if (value < 8000 && def.nums[0] === undefined) { + def.nums[0] = parseInt(player.p.c_roll * 10000) % 10; + change = true; + } + if (value < 6400 && def.nums[1] === undefined) { + def.nums[1] = parseInt(player.p.c_roll * 1000) % 10; + change = true; + } + if (value < 5000 && def.nums[2] === undefined) { + def.nums[2] = parseInt(player.p.c_roll * 100) % 10; + change = true; + } + if (value < 3000 && def.nums[3] === undefined) { + def.nums[3] = parseInt(player.p.c_roll * 10); + change = true; + } + if (value < 2200 && player.p.c_item && !def.success) { + def.success = true; + change = true; + } + if (value < 2200 && player.p.c_itemx && !def.failure) { + def.failure = true; + change = true; + } + if (change) { + player.socket.emit("q_data", { q: player.q, num: ref.num, p: def }); + } + } + } + if (player.q[name].ms <= 0) { + delete player.q[name]; + if (name == "exchange") { + if (player.items[ref.num] && player.items[ref.num].name == "placeholder") { + player.citems[ref.num] = player.items[ref.num] = null; + } + exchange(player, ref.id, { v: ref.v }); + if (ref.qs) { + player.socket.emit("game_response", { response: ref.qs + "_success", suffix: ref.s || "" }); + } else { + xy_emit(G.maps.main.exchange, "upgrade", { type: "exchange", success: 1 }); + } + resend(player, "reopen+u+cid"); + } + 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++; + } + } + 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; + 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++; + } + 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; + } + + 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"); + } + 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"); + } + } + } + } + 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); + 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); + } 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" }); + } + } + } + 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() < max(0, prop.breaks / 100.0)) { + player.cid++; + player.u = true; + player.cslots.mainhand = player.slots.mainhand = null; + player.socket.emit("game_log", "Your rod broke down ..."); + player.socket.emit("game_response", { response: "data", cevent: "item_break", slot: "mainhand" }); + } + } + player.socket.emit("game_response", { response: "data", cevent: "fishing_success", slot: "mainhand" }); + resend(player, "reopen"); + } else { + player.socket.emit("ui", { name: player.name, type: "fishing_none", cevent: true }); + } + } + if (name == "mining") { + if (Math.random() < 0.2) { + if (player.esize) { + exchange(player, ref.drop, { phrase: "Mined" }); + } + consume_skill(player, "mining", true); + if (player.slots.mainhand) { + var prop = calculate_item_properties(player.slots.mainhand); + if (prop.breaks && Math.random() < max(0, prop.breaks / 100.0)) { + player.cid++; + player.u = true; + player.cslots.mainhand = player.slots.mainhand = null; + player.socket.emit("game_log", "Your pickaxe broke down ..."); + } + } + resend(player, "reopen"); + } else { + player.socket.emit("ui", { type: "mining_none" }); + } + } + if (name == "revival") { + player.rip = false; + invincible_logic(player); + if (player.party) { + send_party_update(player.party); + } + resend(player, "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; + // 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); + defeat_player(player); + 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(); + xy_u_logic(player); + xy_upush_logic(player); + if (!player.npc) { + pmap_move(player); + } + } else { + player.red_zone *= 0.97; + } + 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() < player.aggro_diff) { + return; + } + if (monster.rage && Math.random() < monster.rage - player.aggro_diff) { + target_player(monster, player); + } + var attack = commence_attack(monster, player, "attack"); + if (attack && attack.events && attack.events.length) { + if (monster_map[monster.id]) { + if (!monster_map[monster.id].data.events) { + monster_map[monster.id].data.events = attack.events; + } + monster_map[monster.id].data.events.push(...attack.events); + } else { + to_push.push({ id: monster.id, entity: monster, data: monster_to_client(monster, attack.events) }); + monster_map[monster.id] = to_push[to_push.length - 1]; + } + } + // disappearing_text(player.socket,monster,"ATTACK!",{color:"#E082B1",xy:1}); + } + }); + } + 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); + player_rip_logic(player); + 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)) { + // player.s["debuffaura"]={"ms":200,"name":"Debuff","skin":"citizens","citizens":true,"speed":-40}; + 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); + } } 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; - 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); - } - } + 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; + 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(); - - 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))); + 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); + } + } + + 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(); - - 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 (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]); - } - } - 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.hp < player.max_hp * 0.275 && player.hp < min_val) { - min_val = player.hp; - target = player; - } - if (def.seek == "cuteness" && player.cuteness && player.cuteness > 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++; - } - } - - 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.hp < player.max_hp) { - commence_attack(npc, player, "partyheal"); - } - } - } - - if (npc.focus) { - var target = get_player(npc.focus); - if (!target || distance(target, npc) < 30) { - continue; - } - npc.going_x = (npc.x + target.x) / 2; - npc.going_y = (npc.y + target.y) / 2; - if (!can_move(npc)) { - npc.going_x = npc.x + moves[0][0] * multiplier; - npc.going_y = npc.y + moves[0][1] * multiplier; - } - } else { - npc.going_x = npc.x + moves[0][0] * multiplier; - npc.going_y = npc.y + moves[0][1] * multiplier; - } - npc.d_multiplier = 1 + 2 * Math.random(); - - 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(); - } - } - 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 + // 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]; + 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 (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]); + } + } + 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.hp < player.max_hp * 0.275 && player.hp < min_val) { + min_val = player.hp; + target = player; + } + if (def.seek == "cuteness" && player.cuteness && player.cuteness > 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++; + } + } + + 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.hp < player.max_hp) { + commence_attack(npc, player, "partyheal"); + } + } + } + + if (npc.focus) { + var target = get_player(npc.focus); + if (!target || distance(target, npc) < 30) { + continue; + } + npc.going_x = (npc.x + target.x) / 2; + npc.going_y = (npc.y + target.y) / 2; + if (!can_move(npc)) { + npc.going_x = npc.x + moves[0][0] * multiplier; + npc.going_y = npc.y + moves[0][1] * multiplier; + } + } else { + npc.going_x = npc.x + moves[0][0] * multiplier; + npc.going_y = npc.y + moves[0][1] * multiplier; + } + npc.d_multiplier = 1 + 2 * Math.random(); + + 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(); + } + } + 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(); - // 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]; - } - } - - 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 + // 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]; + } + } + + 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 { - 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"); - } - } - } - } catch (e) { - log_trace("#X aura loop error", e); - } - setTimeout(aura_loop, 1000); + 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"); + } + } + } + } catch (e) { + log_trace("#X aura loop error", e); + } + setTimeout(aura_loop, 1000); } setTimeout(game_loop, 10); @@ -13005,773 +13005,773 @@ 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), - ); - } - } - } - } catch (e) { - log_trace("#X observer loop2 error", e); - } + 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); 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); - } + 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"); - } - } - }, - 3 * 60 * 60 * 1000, - ); //3 - }, - 1 * 60 * 60 * 1000, + 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"); + } + } + }, + 3 * 60 * 60 * 1000, + ); //3 + }, + 1 * 60 * 60 * 1000, ); //1 setInterval(function () { - try { - var edge = future_s(-120); - for (var id in players) { - if (0 && players[id].last_ipass < edge) { - players[id].ban = "ipass"; - players[id].socket.emit("disconnect_reason", "Failed to check in. Your network might be too slow."); - players[id].socket.disconnect(); - } - } - } catch (e) { - log_trace("#X ipass loop error", e); - } + try { + var edge = future_s(-120); + for (var id in players) { + if (0 && players[id].last_ipass < edge) { + players[id].ban = "ipass"; + players[id].socket.emit("disconnect_reason", "Failed to check in. Your network might be too slow."); + players[id].socket.disconnect(); + } + } + } catch (e) { + log_trace("#X ipass loop error", e); + } }, 132000); 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(); - } - } - } catch (e) { - log_trace("#X pause loop error", e); - } - }, 2000); + 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(); + } + } + } 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) { - send_party_update(oname); - } - } catch (e) { - log_trace("#X party loop error", e); - } + try { + for (var oname in parties) { + send_party_update(oname); + } + } 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; - player.p.minutes++; - 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); - }, - ); - } 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") || "")); - } - }); - } - } catch (e) { - log_trace("#X decay loop error", e); - } + 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) { + 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); + }, + ); + } 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") || "")); + } + }); + } + } 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; - } - } catch (e) { - log_trace("#X ascore loop error", e); - } + try { + for (var id in a_score) { + a_score[id] *= 0.99; + } + } 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); - } - } catch (e) { - log_trace("#X xrange loop error", e); - } + 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); 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 }; - } - } - } catch (e) { - log_trace("#X first_character loop error", e); - } + 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 }; + } + } + } 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]; - delete projectiles[id]; - complete_attack(projectile.attacker, projectile.target, projectile); - } - } catch (e) { - log_trace("#X projectile loop error", e); - } - } + 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); + } + } catch (e) { + log_trace("#X projectile loop error", e); + } + } } setInterval(projectiles_loop, 7); var accel = 1; if (mode.fast_mlevels) { - accel = 100; + 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); - } + 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); 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 < 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); - } + 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 < 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); 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 (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; 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); - } + 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 (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; 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 setInterval( - function () { - try { - server_loot(); - } catch (e) { - log_trace("#X server loot error", e); - } - }, - 4 * 60 * 60 * 1000, + 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); - } + 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(); - } - 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(); - } - } - 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); - }, - ); - } - 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) { - 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) {} - } - 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(); - 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; - }, - ); - } - - 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); - } - } - 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); - } - } + 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(); + } + } + 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); + }, + ); + } + 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) { + 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) {} + } + 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(); + 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; + }, + ); + } + + 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); + } + } + 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); + } + } } 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) { - 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(); - server_log("Server update sent"); - delete server.update_call; - }, - 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(); - } + // #IMPORTANT: sometimes none of the success/error callbacks trigger [20/08/19] + 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(); + server_log("Server update sent"); + delete server.update_call; + }, + 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(); + } } setInterval(sync_loop, 24000); setInterval(server_loop, 1000); function shutdown() { - server_log("shutdown", 1); - server.live = false; - if (!server.exists) { - server_loot("all"); - } - sync_loop(); + 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; - monster.cid++; - 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); - } - }); - for (var i = 0; i < seconds; i++) { - function m(x) { - return function () { - broadcast("server_message", { message: "Server shutdown in: " + x, color: "#DD1B50", nod: true, log: true }); - }; - } - setTimeout(m(seconds - i), i * 1000); - } - broadcast("eval", { code: "call_code_function('trigger_event','shutdown',{seconds:" + seconds + "})" }); - setTimeout(shutdown, seconds * 1000); - if (!is_sdk && region == "EU" && server_name == "I") { - discord_call("Game update sequence initiated. Servers are shutting down in 20 seconds!"); - } + 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; + } + } + } + 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; i < seconds; i++) { + function m(x) { + return function () { + broadcast("server_message", { message: "Server shutdown in: " + x, color: "#DD1B50", nod: true, log: true }); + }; + } + setTimeout(m(seconds - i), i * 1000); + } + broadcast("eval", { code: "call_code_function('trigger_event','shutdown',{seconds:" + seconds + "})" }); + setTimeout(shutdown, seconds * 1000); + if (!is_sdk && region == "EU" && server_name == "I") { + discord_call("Game update sequence initiated. Servers are shutting down in 20 seconds!"); + } } function exit_handler(options, err) { - if (options.exit) { - server_log("exit_handler", 1); - shutdown_routine(); - } + if (options.exit) { + server_log("exit_handler", 1); + shutdown_routine(); + } } process.on("exit", exit_handler.bind(null, { cleanup: true })); process.on("SIGHUP", exit_handler.bind(null, { exit: true })); diff --git a/node/server_functions.js b/node/server_functions.js index d158207f..9ef2f8c8 100644 --- a/node/server_functions.js +++ b/node/server_functions.js @@ -4,13 +4,13 @@ var range_check = require("range_check"); var protobuf = require("protobufjs"); var ByteBuffer = require("bytebuffer"); // Steam decryption var false_socket = { - emit: function (a, b) { - if (is_sdk && !server.shutdown) { - console.log([a, b]); - } - }, - total_calls: 0, - calls: [], + emit: function (a, b) { + if (is_sdk && !server.shutdown) { + console.log([a, b]); + } + }, + total_calls: 0, + calls: [], }; var current_socket = false_socket; var call_modifier = 1; @@ -18,3514 +18,3514 @@ var ls_method = ""; var server_start = new Date(); var timers = { - pinkgoo: false, - snowman: false, - wabbit: false, - hardcore: new Date(), - hide_and_seek: false, - cyberland: new Date(), + pinkgoo: false, + snowman: false, + wabbit: false, + hardcore: new Date(), + hide_and_seek: false, + cyberland: new Date(), }; var npcs = {}; var signups = {}; var hide_and_seek = {}; var stats = { - kills: {}, + kills: {}, }; var edges = { - next_goldenbat: 80000, - next_cutebee: 480000, + next_goldenbat: 80000, + next_cutebee: 480000, }; var NPC_prefix = "NPC0000000000000000NPC"; function sprocess_game_data() { - for (var name in G.items) { - var def = G.items[name]; - def.igrade = calculate_item_grade(def); - if (!def.igrade) { - def.igrace = 1; - } else if (def.igrade == 1) { - def.igrace = -1; - } else if (def.igrade == 2) { - def.igrace = -2; - } - - def.a = 0; - - if (def.compound) { - var u_v = calculate_item_value({ name: name, level: 3 + (def.edge || 0) }); - def.a = parseInt(round(u_v / 5000000)); - } else if (def.upgrade) { - var u_v = calculate_item_value({ name: name, level: 7 + (def.edge || 0) }); - def.a = parseInt(round(u_v / 5000000)); - } else { - var u_v = calculate_item_value({ name: name }); - def.a = parseInt(round(u_v / 5000000)); - } - if (def.event) { - def.a = 2; - } - if (def.rare) { - def.a = 12; - } - } - G.items.lostearring.igrade = 2; - if (gameplay == "test") { - test_logic(); - } - if (gameplay == "hardcore") { - hardcore_logic(); - for (var m in D.drops.monsters) { - for (var i = 0; i < D.drops.monsters[m].length; i++) { - D.drops.monsters[m][i][0] *= 200; - if (D.drops.monsters[m][i][0] < 0.0001) { - D.drops.monsters[m][i][0] *= 12.0; - } - D.drops.monsters[m][i][0] = min(1, D.drops.monsters[m][i][0]); - } - - D.drops.monsters[m].push([1.0 / 100, "glitch"]); - D.drops.monsters[m].push([1.0 / 100, "glitch"]); - D.drops.monsters[m].push([1.0 / 1000, "glitch"]); - } - 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; i < D.drops.maps[mname].length; i++) { - D.drops.maps[mname][i][0] *= 200; - D.drops.maps[mname][i][0] = min(1, D.drops.maps[mname][i][0]); - } - - D.drops.maps[mname].push([1.0 / 1200, "gem0"]); - } - D.drops.gem0.push([0.5, "candycane"]); - D.drops.gem0.push([0.05, "basketofeggs"]); - D.drops.monsters.iceroamer.push([0.1, "open", "statbelt"]); - D.drops.monsters.arcticbee.push([0.00001, "fclaw"]); - D.drops.monsters.minimush.push([0.00001, "throwingstars"]); - D.drops.monsters.squig.push([0.000008, "glitch"]); - D.drops.monsters.bbpompom.push([0.00008, "glitch"]); - D.drops.monsters.croc.push([0.000008, "glitch"]); - D.drops.monsters.mole.push([0.002, "gemfragment"]); - D.drops.monsters.mole.push([0.002, "gemfragment"]); - D.drops.maps.mansion.push([0.001, "lostearring"]); - D.drops.maps.mansion.push([0.001, "lostearring"]); - D.drops.monsters.croc.push([1, "seashell"]); - D.drops.monsters.croc.push([1, "seashell"]); - D.drops.maps.global = []; - D.monster_gold.bat *= 50; - D.monster_gold.bbpompom *= 75; - D.monster_gold.ghost *= 50; - events.egghunt = 0.1; - } - D.craftmap = {}; - for (var name in G.craft) { - var items = []; - G.craft[name].items.forEach(function (x) { - var name = x[1]; - if (x[2]) { - name += "+" + x[2]; - } - items.push(name); - }); - items.sort(); - var key = items.join(","); - D.craftmap[key] = name; - } - process_game_data(); - for (var name in G.monsters) { - stats.kills[name] = stats.kills[name] || 0; - } - D.base_gold = {}; - for (var mname in G.maps) { - var map = G.maps[mname]; - (map.monsters || []).forEach(function (pack) { - var def = G.monsters[pack.type]; - var drop_value = 0; - var map_value = 0; - var global_value = 0; - var hp_mult = def.hp / 1000.0; - // #BETTER IDEA# - // track damage done + dps utilisation / pack fullness - calculate gold based on that - // /1400.0 /40.000 /14000.0 originally - // 0.0624 is the original modifier - pack.gold = - def.hp * - (0.0782 + - max(def.charge - 40, 0) / 1900.0 + - ((1 - dps_multiplier(def.attack + (def.apiercing || 0) / 2 + (def.rpiercing || 0) / 2)) * 1000) / 120000.0 + - min( - (1 - damage_multiplier(def.armor || 0)) * 1000 + (def.evasion || 0) * 10 + (def.avoidance || 0) * 10, - (1 - damage_multiplier(def.resistance || 0)) * 1000 + - (def.reflection || 0) * 25 + - (def.avoidance || 0) * 10, - ) / - 12000.0) - - def.xp * 0.0172; - if ((map.pvp || map.safe_pvp) && !is_pvp) { - pack.gold *= 2; - } - drop_value = calculate_xvalue(D.drops.monsters[pack.type] || []); - map_value = calculate_xvalue(D.drops.maps[mname] || [], undefined, undefined, hp_mult); - global_value = calculate_xvalue(D.drops.maps.global || [], undefined, undefined, hp_mult); - pack.gold = pack.gold - map_value / 1.002 - drop_value * 1.002; // Originally 1.2 - - if (def.hp <= 2000 && pack.gold < def.hp * 0.0482) { - pack.gold = def.hp * 0.0482; - } else if (pack.gold < def.hp * 0.02808) { - pack.gold = def.hp * 0.02808; - } - - //if(def.hp<2000 && pack.gold0.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, - ); - }); - } - 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 (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; - } + for (var name in G.items) { + var def = G.items[name]; + def.igrade = calculate_item_grade(def); + if (!def.igrade) { + def.igrace = 1; + } else if (def.igrade == 1) { + def.igrace = -1; + } else if (def.igrade == 2) { + def.igrace = -2; + } + + def.a = 0; + + if (def.compound) { + var u_v = calculate_item_value({ name: name, level: 3 + (def.edge || 0) }); + def.a = parseInt(round(u_v / 5000000)); + } else if (def.upgrade) { + var u_v = calculate_item_value({ name: name, level: 7 + (def.edge || 0) }); + def.a = parseInt(round(u_v / 5000000)); + } else { + var u_v = calculate_item_value({ name: name }); + def.a = parseInt(round(u_v / 5000000)); + } + if (def.event) { + def.a = 2; + } + if (def.rare) { + def.a = 12; + } + } + G.items.lostearring.igrade = 2; + if (gameplay == "test") { + test_logic(); + } + if (gameplay == "hardcore") { + hardcore_logic(); + for (var m in D.drops.monsters) { + for (var i = 0; i < D.drops.monsters[m].length; i++) { + D.drops.monsters[m][i][0] *= 200; + if (D.drops.monsters[m][i][0] < 0.0001) { + D.drops.monsters[m][i][0] *= 12.0; + } + D.drops.monsters[m][i][0] = min(1, D.drops.monsters[m][i][0]); + } + + D.drops.monsters[m].push([1.0 / 100, "glitch"]); + D.drops.monsters[m].push([1.0 / 100, "glitch"]); + D.drops.monsters[m].push([1.0 / 1000, "glitch"]); + } + 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; i < D.drops.maps[mname].length; i++) { + D.drops.maps[mname][i][0] *= 200; + D.drops.maps[mname][i][0] = min(1, D.drops.maps[mname][i][0]); + } + + D.drops.maps[mname].push([1.0 / 1200, "gem0"]); + } + D.drops.gem0.push([0.5, "candycane"]); + D.drops.gem0.push([0.05, "basketofeggs"]); + D.drops.monsters.iceroamer.push([0.1, "open", "statbelt"]); + D.drops.monsters.arcticbee.push([0.00001, "fclaw"]); + D.drops.monsters.minimush.push([0.00001, "throwingstars"]); + D.drops.monsters.squig.push([0.000008, "glitch"]); + D.drops.monsters.bbpompom.push([0.00008, "glitch"]); + D.drops.monsters.croc.push([0.000008, "glitch"]); + D.drops.monsters.mole.push([0.002, "gemfragment"]); + D.drops.monsters.mole.push([0.002, "gemfragment"]); + D.drops.maps.mansion.push([0.001, "lostearring"]); + D.drops.maps.mansion.push([0.001, "lostearring"]); + D.drops.monsters.croc.push([1, "seashell"]); + D.drops.monsters.croc.push([1, "seashell"]); + D.drops.maps.global = []; + D.monster_gold.bat *= 50; + D.monster_gold.bbpompom *= 75; + D.monster_gold.ghost *= 50; + events.egghunt = 0.1; + } + D.craftmap = {}; + for (var name in G.craft) { + var items = []; + G.craft[name].items.forEach(function (x) { + var name = x[1]; + if (x[2]) { + name += "+" + x[2]; + } + items.push(name); + }); + items.sort(); + var key = items.join(","); + D.craftmap[key] = name; + } + process_game_data(); + for (var name in G.monsters) { + stats.kills[name] = stats.kills[name] || 0; + } + D.base_gold = {}; + for (var mname in G.maps) { + var map = G.maps[mname]; + (map.monsters || []).forEach(function (pack) { + var def = G.monsters[pack.type]; + var drop_value = 0; + var map_value = 0; + var global_value = 0; + var hp_mult = def.hp / 1000.0; + // #BETTER IDEA# + // track damage done + dps utilisation / pack fullness - calculate gold based on that + // /1400.0 /40.000 /14000.0 originally + // 0.0624 is the original modifier + pack.gold = + def.hp * + (0.0782 + + max(def.charge - 40, 0) / 1900.0 + + ((1 - dps_multiplier(def.attack + (def.apiercing || 0) / 2 + (def.rpiercing || 0) / 2)) * 1000) / 120000.0 + + min( + (1 - damage_multiplier(def.armor || 0)) * 1000 + (def.evasion || 0) * 10 + (def.avoidance || 0) * 10, + (1 - damage_multiplier(def.resistance || 0)) * 1000 + + (def.reflection || 0) * 25 + + (def.avoidance || 0) * 10, + ) / + 12000.0) - + def.xp * 0.0172; + if ((map.pvp || map.safe_pvp) && !is_pvp) { + pack.gold *= 2; + } + drop_value = calculate_xvalue(D.drops.monsters[pack.type] || []); + map_value = calculate_xvalue(D.drops.maps[mname] || [], undefined, undefined, hp_mult); + global_value = calculate_xvalue(D.drops.maps.global || [], undefined, undefined, hp_mult); + pack.gold = pack.gold - map_value / 1.002 - drop_value * 1.002; // Originally 1.2 + + if (def.hp <= 2000 && pack.gold < def.hp * 0.0482) { + pack.gold = def.hp * 0.0482; + } else if (pack.gold < def.hp * 0.02808) { + pack.gold = def.hp * 0.02808; + } + + //if(def.hp<2000 && pack.gold0.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, + ); + }); + } + 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 (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; + } } function calculate_xvalue(arr, rec, divide, mult) { - var value = 0; - var total = 0; - // console.log(arr); - 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]]; - 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; - } - if (!value) { - console.log(drop); - } - }); - return value; + var value = 0; + var total = 0; + // console.log(arr); + 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]]; + 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; + } + 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; - 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) { + 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]); } 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); + 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; - } - return false; + if (player.bot || player.p.free || player.s.licenced || player.role == "gm") { + return true; + } + return false; } 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; - 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; - } - } - return true; + if (gameplay == "hardcore") { + //if(player.type=="mage" || player.type=="priest") return false; + //if(player.type=="rogue") 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; + } + } + 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); - } + 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 }); - }); + 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; - } - return true; + // 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; - } - return false; + 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; - } - return false; + 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; - } - return false; + 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; - } - return false; + 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; - } - return false; + 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); - } - } - } - player.s[name].ms -= 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; + } + } } function reset_player(player, soft) { - if (!player.p.hardcore) { - player.p = { hardcore: true, dt: {} }; - player.max_stats = { monsters: {} }; - } - 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; - } - } + if (!player.p.hardcore) { + player.p = { hardcore: true, dt: {} }; + player.max_stats = { monsters: {} }; + } + 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; + } + } } 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, - }; + 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); - 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 < S.sold.length; i++) { - if (done) { - return; - } - if (can_stack(S.sold[i], new_item)) { - done = true; - S.sold[i].q = S.sold[i].q + new_item.q; - csold[i] = cache_item(S.sold[i], true); - } - if (S.sold[i].name == new_item.name && S.sold[i].level == new_item.level) { - if (!S.sold[i].p) { - replace = i; - } - count += 1; - } - } - 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); - } + 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 < S.sold.length; i++) { + if (done) { + return; + } + if (can_stack(S.sold[i], new_item)) { + done = true; + S.sold[i].q = S.sold[i].q + new_item.q; + csold[i] = cache_item(S.sold[i], true); + } + if (S.sold[i].name == new_item.name && S.sold[i].level == new_item.level) { + if (!S.sold[i].p) { + replace = i; + } + count += 1; + } + } + 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; - var count = 0; - var replace = null; - item.rid = randomStr(5); - for (var i = 0; i < S.found.length; i++) { - if (done) { - return; - } - if (can_stack(S.found[i], item)) { - done = true; - S.found[i].q = S.found[i].q + item.q; - cfound[i] = cache_item(S.found[i], true); - } - if (S.found[i].name == item.name && S.found[i].level == item.level) { - if (!S.found[i].p) { - replace = i; - } - count += 1; - } - } - 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); - } + var done = false; + var count = 0; + var replace = null; + item.rid = randomStr(5); + for (var i = 0; i < S.found.length; i++) { + if (done) { + return; + } + if (can_stack(S.found[i], item)) { + done = true; + S.found[i].q = S.found[i].q + item.q; + cfound[i] = cache_item(S.found[i], true); + } + if (S.found[i].name == item.name && S.found[i].level == item.level) { + if (!S.found[i].p) { + replace = i; + } + count += 1; + } + } + 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; 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; - } - 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); - }); - } - } + 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; + } + 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); + }); + } + } } 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; + 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; + 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; + if (is_same(player, seller)) { + return; + } + player.xp += tax * 3.2; } function normalise(data) { - // normalise(data,["gold","gold"],["num","inv"]) - for (var i = 1; i < arguments.length; i++) { - var def = arguments[i]; - var name = def[0]; - var type = def[1]; - var value = data[name]; - if (type == "gold") { - value = max(0, min(parseInt(value) || 0, 99999999999)); - } - data[name] = value; - } + // normalise(data,["gold","gold"],["num","inv"]) + for (var i = 1; i < arguments.length; i++) { + var def = arguments[i]; + var name = def[0]; + var type = def[1]; + var value = data[name]; + if (type == "gold") { + value = max(0, min(parseInt(value) || 0, 99999999999)); + } + data[name] = value; + } } var cloudflare_ips = [ - // https://www.cloudflare.com/ips/ - "103.21.244.0/22", - "103.22.200.0/22", - "103.31.4.0/22", - "104.16.0.0/12", - "108.162.192.0/18", - "131.0.72.0/22", - "141.101.64.0/18", - "162.158.0.0/15", - "172.64.0.0/13", - "173.245.48.0/20", - "188.114.96.0/20", - "190.93.240.0/20", - "197.234.240.0/22", - "198.41.128.0/17", - "2400:cb00::/32", - "2405:b500::/32", - "2606:4700::/32", - "2803:f800::/32", - "2c0f:f248::/32", - "2a06:98c0::/29", + // https://www.cloudflare.com/ips/ + "103.21.244.0/22", + "103.22.200.0/22", + "103.31.4.0/22", + "104.16.0.0/12", + "108.162.192.0/18", + "131.0.72.0/22", + "141.101.64.0/18", + "162.158.0.0/15", + "172.64.0.0/13", + "173.245.48.0/20", + "188.114.96.0/20", + "190.93.240.0/20", + "197.234.240.0/22", + "198.41.128.0/17", + "2400:cb00::/32", + "2405:b500::/32", + "2606:4700::/32", + "2803:f800::/32", + "2c0f:f248::/32", + "2a06:98c0::/29", ]; function get_ip_raw(player) { - if (!player.socket) { - player = { socket: player }; - } // so get_ip(socket) works too [06/09/18] - // return player.socket.handshake.address; - // BEWARE: player.socket.request.connection.remoteAddress - try { - if (player.last_ip) { - return player.last_ip; - } - if (player.first_ip) { - return player.first_ip; - } - if (player.socket.request["headers"]["cf-connecting-ip"]) { - if (range_check.inRange(range_check.displayIP(player.socket.handshake.address), cloudflare_ips)) { - player.first_ip = player.socket.request["headers"]["cf-connecting-ip"]; - return player.first_ip; - } else { - player.ipx = -1; - } - } - } catch (e) {} - try { - return player.socket.request.connection.remoteAddress || player.socket.handshake.address; - } catch (e) {} - try { - return player.socket.handshake.address; - } catch (e) {} + if (!player.socket) { + player = { socket: player }; + } // so get_ip(socket) works too [06/09/18] + // return player.socket.handshake.address; + // BEWARE: player.socket.request.connection.remoteAddress + try { + if (player.last_ip) { + return player.last_ip; + } + if (player.first_ip) { + return player.first_ip; + } + if (player.socket.request["headers"]["cf-connecting-ip"]) { + if (range_check.inRange(range_check.displayIP(player.socket.handshake.address), cloudflare_ips)) { + player.first_ip = player.socket.request["headers"]["cf-connecting-ip"]; + return player.first_ip; + } else { + player.ipx = -1; + } + } + } catch (e) {} + try { + return player.socket.request.connection.remoteAddress || player.socket.handshake.address; + } catch (e) {} + try { + return player.socket.handshake.address; + } catch (e) {} } function get_ip(player) { - var ip = get_ip_raw(player) || ""; - return ip.replace("::ffff:", ""); + var ip = get_ip_raw(player) || ""; + return ip.replace("::ffff:", ""); } function quick_hash(str) { - var hash = 0; - if (str.length == 0) { - return hash; - } - for (var i = 0; i < str.length; i++) { - var char = str.charCodeAt(i); - hash = (hash << 5) - hash + char; - hash = hash & hash; - } - return hash; + var hash = 0; + if (str.length == 0) { + return hash; + } + for (var i = 0; i < str.length; i++) { + var char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash = hash & hash; + } + return hash; } function verify_steam_ticket(player, ticket) { - // Thanks: https://github.com/DoctorMcKay/node-steam-user - try { - var outer = EncryptedAppTicket.decode(new Buffer(ticket, "hex")); - var decrypted = symmetricDecrypt(outer.encryptedTicket, new Buffer(variables.steam_key, "hex")); - let userData = decrypted.slice(0, outer.cbEncrypteduserdata); - let ownershipTicketLength = decrypted.readUInt32LE(outer.cbEncrypteduserdata); - let ownershipTicket = parseAppTicket( - decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), - ); - if (ownershipTicket) { - ownershipTicket.userData = userData.toString(); - } - if (ownershipTicket.appID == 777150 && ownershipTicket.steamID) { - player.auth_type = "steam"; - player.auth_id = ownershipTicket.steamID; - player.p.steam_id = ownershipTicket.steamID; - delete player.s.authfail; - } - } catch (e) { - console.log("#A verify_steam_ticket: " + e); - } + // Thanks: https://github.com/DoctorMcKay/node-steam-user + try { + var outer = EncryptedAppTicket.decode(new Buffer(ticket, "hex")); + var decrypted = symmetricDecrypt(outer.encryptedTicket, new Buffer(variables.steam_key, "hex")); + let userData = decrypted.slice(0, outer.cbEncrypteduserdata); + let ownershipTicketLength = decrypted.readUInt32LE(outer.cbEncrypteduserdata); + let ownershipTicket = parseAppTicket( + decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), + ); + if (ownershipTicket) { + ownershipTicket.userData = userData.toString(); + } + if (ownershipTicket.appID == 777150 && ownershipTicket.steamID) { + player.auth_type = "steam"; + player.auth_id = ownershipTicket.steamID; + player.p.steam_id = ownershipTicket.steamID; + delete player.s.authfail; + } + } catch (e) { + console.log("#A verify_steam_ticket: " + e); + } } 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"; - //player.mas_receipt=receipt; - var content = { "receipt-data": receipt, password: variables.apple_token }; - var url = "https://buy.itunes.apple.com/verifyReceipt"; - request( - { - url: url, - method: "POST", - json: content, - }, - 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) { - 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); - delete player.s.authfail; - } else { - delete player.temp_auth; - console.log("#M: mas declined: " + player.name + " " + (body && body.status)); - } - }, - ); - } + 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"; + //player.mas_receipt=receipt; + var content = { "receipt-data": receipt, password: variables.apple_token }; + var url = "https://buy.itunes.apple.com/verifyReceipt"; + request( + { + url: url, + method: "POST", + json: content, + }, + 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) { + 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); + delete player.s.authfail; + } else { + delete player.temp_auth; + 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) { - console.log(body); - }); + 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); - 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) { - console.log(body); - }); + 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) { + 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) }; - } + 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) { - delete player.s.hopsickness; - 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 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; - } - calculate_player_stats(player); - } - var c = [region + server_name, "" + new Date()]; - player.p.entries.push(c); - if (player.p.entries.length > 60) { - player.p.entries.shift(); - } + delete player.s.hopsickness; + 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 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; + } + calculate_player_stats(player); + } + var c = [region + server_name, "" + new Date()]; + player.p.entries.push(c); + 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; - } - 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.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"); + } } 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; + //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; + 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]; - 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; + 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; + 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]) { - return G.maps[iid].ref[id]; - } - } - return { x: 0, y: 0, map: "main", in: "main" }; + 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" }; } 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) {} + 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) {} } function pmap_get(player) { - var hash = ghash(player, 6); - if (!instances[player.in].pmap[hash]) { - return {}; - } - return instances[player.in].pmap[hash]; + 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; + 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; - } - pmap_remove(player); - pmap_add(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, - }, - gain: 0, - players: {}, - }; - 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.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; + 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: {}, + }; + 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.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; - } - } - return gold; + 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; - } - return 2; + 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"); - } - } 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 }); - } - } 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 = {}; - 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); - } + 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"); + } + } 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 }); + } + } 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 = {}; + 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]; - } - } - - 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; + 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]; + } + } + + 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); - instance.rage_list.push(map_def); - } - for (var j = 0; j < map_def.count; j++) { - if (G.monsters[map_def.type].announce) { - setTimeout(new_monster_f(name, map_def), 120 * 1000); - } else { - new_monster(name, map_def); - } - } - } - // for(var i=0;i<(map.specials||[]).length;i++) - // { - // var map_def=map.specials[i]; - // new_monster(name,map_def,1); - // } - // console.log(JSON.stringify(instance.players)); - for (var i = 0; i < (map.npcs || []).length; i++) { - var map_def = map.npcs[i]; - var def = G.npcs[map_def.id]; - if (instance.players[NPC_prefix + def.name]) { - console.log("NPC NAME CLASH: " + def.name); - shutdown_routine(); - } - var npc = (instance.players[NPC_prefix + def.name] = create_npc(def, map_def, instance)); - npcs[map_def.id] = npc; - instance.npcs += 1; - } - 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); - return instance; + 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 < map_def.count; j++) { + if (G.monsters[map_def.type].announce) { + setTimeout(new_monster_f(name, map_def), 120 * 1000); + } else { + new_monster(name, map_def); + } + } + } + // for(var i=0;i<(map.specials||[]).length;i++) + // { + // var map_def=map.specials[i]; + // new_monster(name,map_def,1); + // } + // console.log(JSON.stringify(instance.players)); + for (var i = 0; i < (map.npcs || []).length; i++) { + var map_def = map.npcs[i]; + var def = G.npcs[map_def.id]; + if (instance.players[NPC_prefix + def.name]) { + console.log("NPC NAME CLASH: " + def.name); + shutdown_routine(); + } + var npc = (instance.players[NPC_prefix + def.name] = create_npc(def, map_def, instance)); + npcs[map_def.id] = npc; + instance.npcs += 1; + } + 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); + return instance; } function pause_instance(instance) { - server_log("Paused: " + instance.name); - instance.paused = true; + server_log("Paused: " + instance.name); + instance.paused = true; } function resume_instance(instance) { - if (!instance.paused) { - return; - } - // server_log("Resumed: "+instance.name); - instance.paused = false; - update_instance(instance); + if (!instance.paused) { + return; + } + // server_log("Resumed: "+instance.name); + 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"); - } - restore_state(player); - 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 - } - server_log("Deleted an instance of " + instances[name].map, 1); - delete instances[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 + } + 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); + 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] - }); - }); - 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 }; - } - }); - }); - 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 }); - } - }); - }); - 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 == "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 == "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; - } - } - 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 }); - } - 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 }); - } - 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; - //server_log("Love Goo: "+JSON.stringify(pack)); - 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 }); - } - }); - }); - 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); - // 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 }); - } - }); - }); - 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); - } + 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 }); + } + 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 }); + } + 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 }); + } + 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 == "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 = {}; + 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; + } + } + 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 }); + } + 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 }); + } + 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; + //server_log("Love Goo: "+JSON.stringify(pack)); + 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 }); + } + }); + }); + 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); + // 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 }); + } + }); + }); + 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); + } } 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 == "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 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 == "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(); - 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.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; - } - }); - - 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; - var 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(); - } - } - } - ["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; - } - 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); - } + 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.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; + } + }); + + 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; + var 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(); + } + } + } + ["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; + } + 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); - } + 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" }); - } + 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" }); - } - } - } - } - }); - 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); - }); - return worker; + 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" }); + } + } + } + } + }); + 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); + }); + 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!", - }, - }; - E.minutes = 340; - if (is_sdk) { - E.minutes = 12; - } - setInterval(hardcore_loop, 60 * 1000); - } - setInterval(event_loop, 1000); - setInterval(tavern_loop, 1000); - workers = [ - new_worker(0), - new_worker(1), - new_worker(2), - new_worker(3), - new_worker(4), - new_worker(5), - new_worker(6), - new_worker(7), - ]; + 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; + } + setInterval(hardcore_loop, 60 * 1000); + } + setInterval(event_loop, 1000); + setInterval(tavern_loop, 1000); + workers = [ + new_worker(0), + new_worker(1), + new_worker(2), + new_worker(3), + new_worker(4), + new_worker(5), + new_worker(6), + new_worker(7), + ]; } 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 < S.sold.length; i++) { - csold[i] = cache_item(S.sold[i], true); - } - for (var i = 0; i < S.found.length; i++) { - cfound[i] = cache_item(S.found[i], true); - } - if (!S.ugrace) { - S.ugrace = []; - S.cgrace = []; - for (var i = 0; i < 25; i++) { - S.ugrace[i] = 24; - S.cgrace[i] = 24; - } - } + 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 < S.sold.length; i++) { + csold[i] = cache_item(S.sold[i], true); + } + for (var i = 0; i < S.found.length; i++) { + cfound[i] = cache_item(S.found[i], true); + } + if (!S.ugrace) { + S.ugrace = []; + S.cgrace = []; + for (var i = 0; i < 25; i++) { + S.ugrace[i] = 24; + S.cgrace[i] = 24; + } + } } function get_monsters(type) { - var l = []; - for (var name in instances) { - for (var id in instances[name].monsters) { - var monster = instances[name].monsters[id]; - if (monster.type == type) { - l.push(monster); - } - } - } - return l; + var l = []; + for (var name in instances) { + for (var id in instances[name].monsters) { + var monster = instances[name].monsters[id]; + if (monster.type == type) { + l.push(monster); + } + } + } + return l; } function get_monster(type) { - return get_monsters(type)[0]; + return get_monsters(type)[0]; } function add_item_property(item, prop) { - if (prop == "legacy") { - return; - } - if (!item.ps) { - item.ps = []; - } - if (item.p && !in_arr(item.p, item.ps)) { - item.ps.push(item.p); - } // backwards compatibility - if (!in_arr(prop, item.ps)) { - item.ps.push(prop); - } - item.p = prop; + if (prop == "legacy") { + return; + } + if (!item.ps) { + item.ps = []; + } + if (item.p && !in_arr(item.p, item.ps)) { + item.ps.push(item.p); + } // backwards compatibility + if (!in_arr(prop, item.ps)) { + item.ps.push(prop); + } + item.p = prop; } function add_condition(target, condition, args) { - var def = G.conditions[condition]; - if (!def) { - return; - } - if (!args) { - args = {}; - } - var response = { response: "condition", name: condition, cevent: true }; - var duration = args.duration || args.ms || def.duration; - if (duration == undefined) { - duration = 1000; - } - var C = { ms: duration }; - if (condition == "poisoned" && target.pnresistance) { - if (Math.random() < (target.pnresistance || 0) / 100.0) { - return xy_emit(target, "ui", { type: "poison_resist", id: target.id }); - } - - duration *= (100 - target.pnresistance) / 100.0; - } - if (condition == "stunned") { - if (Math.random() < (target.phresistance || 0) / 100.0) { - return xy_emit(target, "ui", { type: "stun_resist", id: target.id }); - } - - target.abs = true; - target.moving = false; - } - if (condition == "burned") { - if (Math.random() < (target.firesistance || 0) / 100.0) { - return xy_emit(target, "ui", { type: "fire_resist", id: target.id }); - } - - duration = 5000; - if (!args.attack) { - args.attack = 1000; - } - if (args.divider == 3 && target.s.burned && target.s.burned.ms) { - duration = min( - 12000, - max(duration + 400, min(8000, parseInt(duration / 4 + (50 * args.attack) / (target.s.burned.intensity + 200)))), - ); - } - C.intensity = max( - (target.s.burned && target.s.burned.intensity) || 1, - parseInt(((target.s.burned && target.s.burned.intensity) || 0) / (args.divider || 3) + args.attack), - ); - C.fid = args.fid; - disappearing_text({}, target, "BURN!", { xy: 1, size: "huge", color: "burn", nv: 1 }); //target.is_player&&"huge"||undefined - } - if (condition == "deepfreezed") { - if (Math.random() < (target.fzresistance || 0) / 100.0) { - return xy_emit(target, "ui", { type: "freeze_resist", id: target.id }); - } - } - if (condition == "woven") { - C.s = min((target.is_monster && 20) || 5, (target.s.woven && target.s.woven.s + 1) || 1); - C.speed = -3 * condition.s; - } - duration = max((target.s[condition] && target.s[condition].ms) || 0, duration); - if (target.stresistance && def && def.debuff) { - duration *= (100 - target.stresistance) / 100.0; - } - target.s[condition] = C; - C.ms = response.duration = duration; - server_log(C); - if (args.from) { - C.f = args.from.name; - } - if (args.f) { - C.f = args.f; - } - if (C.f) { - response.from = C.f; - } - target.cid++; - target.u = true; - if (target.socket) { - target.hitchhikers.push(["game_response", response]); - } - return true; + var def = G.conditions[condition]; + if (!def) { + return; + } + if (!args) { + args = {}; + } + var response = { response: "condition", name: condition, cevent: true }; + var duration = args.duration || args.ms || def.duration; + if (duration == undefined) { + duration = 1000; + } + var C = { ms: duration }; + if (condition == "poisoned" && target.pnresistance) { + if (Math.random() < (target.pnresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "poison_resist", id: target.id }); + } + + duration *= (100 - target.pnresistance) / 100.0; + } + if (condition == "stunned") { + if (Math.random() < (target.phresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "stun_resist", id: target.id }); + } + + target.abs = true; + target.moving = false; + } + if (condition == "burned") { + if (Math.random() < (target.firesistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "fire_resist", id: target.id }); + } + + duration = 5000; + if (!args.attack) { + args.attack = 1000; + } + if (args.divider == 3 && target.s.burned && target.s.burned.ms) { + duration = min( + 12000, + max(duration + 400, min(8000, parseInt(duration / 4 + (50 * args.attack) / (target.s.burned.intensity + 200)))), + ); + } + C.intensity = max( + (target.s.burned && target.s.burned.intensity) || 1, + parseInt(((target.s.burned && target.s.burned.intensity) || 0) / (args.divider || 3) + args.attack), + ); + C.fid = args.fid; + disappearing_text({}, target, "BURN!", { xy: 1, size: "huge", color: "burn", nv: 1 }); //target.is_player&&"huge"||undefined + } + if (condition == "deepfreezed") { + if (Math.random() < (target.fzresistance || 0) / 100.0) { + return xy_emit(target, "ui", { type: "freeze_resist", id: target.id }); + } + } + if (condition == "woven") { + C.s = min((target.is_monster && 20) || 5, (target.s.woven && target.s.woven.s + 1) || 1); + C.speed = -3 * condition.s; + } + duration = max((target.s[condition] && target.s[condition].ms) || 0, duration); + if (target.stresistance && def && def.debuff) { + duration *= (100 - target.stresistance) / 100.0; + } + target.s[condition] = C; + C.ms = response.duration = duration; + server_log(C); + if (args.from) { + C.f = args.from.name; + } + if (args.f) { + C.f = args.f; + } + if (C.f) { + response.from = C.f; + } + target.cid++; + target.u = true; + if (target.socket) { + target.hitchhikers.push(["game_response", response]); + } + return true; } function consume_mp(player, mp, target) { - var mult = 1; - if (target && target.humanoid) { - mult = 5; - } - if (player.a.restore_mp && Math.random() < (player.a.restore_mp.attr0 * mult) / 100.0) { - player.mp += mp * 2; - xy_emit(player, "ui", { id: player.id, type: "restore_mp", amount: mp * 2 }); - } else { - player.mp -= (mp * (100 - (player.mp_reduction || 0))) / 100.0; - } - player.mp = parseInt(max(0, player.mp)); - player.mp = min(player.mp, player.max_mp); + var mult = 1; + if (target && target.humanoid) { + mult = 5; + } + if (player.a.restore_mp && Math.random() < (player.a.restore_mp.attr0 * mult) / 100.0) { + player.mp += mp * 2; + xy_emit(player, "ui", { id: player.id, type: "restore_mp", amount: mp * 2 }); + } else { + player.mp -= (mp * (100 - (player.mp_reduction || 0))) / 100.0; + } + player.mp = parseInt(max(0, player.mp)); + player.mp = min(player.mp, player.max_mp); } function game_response(response, data) { - if (!data) { - data = {}; - } - data.response = response; - current_socket.emit("game_response", data); + if (!data) { + data = {}; + } + data.response = response; + current_socket.emit("game_response", data); } function fail_response(response, place, data) { - if (data && is_string(data)) { - data = { reason: data }; - } - if (!response) { - response = "data"; - } - if (is_object(response)) { - data = response; - response = "data"; - place = ls_method; - } - if (is_object(place)) { - data = place; - place = ls_method; - } - if (!data) { - data = {}; - } - if (!place) { - place = ls_method; - } - data.response = response; - data.place = place; - data.failed = true; - current_socket.emit("game_response", data); + if (data && is_string(data)) { + data = { reason: data }; + } + if (!response) { + response = "data"; + } + if (is_object(response)) { + data = response; + response = "data"; + place = ls_method; + } + if (is_object(place)) { + data = place; + place = ls_method; + } + if (!data) { + data = {}; + } + if (!place) { + place = ls_method; + } + data.response = response; + data.place = place; + data.failed = true; + current_socket.emit("game_response", data); } function success_response(response, place, data) { - if (!response) { - response = "data"; - } - if (is_object(response)) { - data = response; - response = "data"; - place = ls_method; - } - if (place && is_object(place)) { - data = place; - place = ls_method; - } - if (!data) { - data = {}; - } - if (!place) { - place = ls_method; - } - if (data.success !== false) { - data.success = true; - } - data.response = response; - data.place = place; - current_socket.emit("game_response", data); + if (!response) { + response = "data"; + } + if (is_object(response)) { + data = response; + response = "data"; + place = ls_method; + } + if (place && is_object(place)) { + data = place; + place = ls_method; + } + if (!data) { + data = {}; + } + if (!place) { + place = ls_method; + } + if (data.success !== false) { + data.success = true; + } + data.response = response; + data.place = place; + current_socket.emit("game_response", data); } function consume_skill(player, name, reuse) { - var penalty_cd = (player.s.penalty_cd && player.s.penalty_cd.ms) || 0; - var multiplier = 1; - //if(name=="attack" || G.skills[name].share=="attack") - if (G.skills[name].share) { - multiplier = G.skills[name].cooldown_multiplier || 1; - name = G.skills[name].share; - } - var cooldown = G.skills[name].cooldown; - if (reuse) { - cooldown = G.skills[name].reuse_cooldown; - } - if (!cooldown) { - return; - } - player.last[name] = future_ms(min(penalty_cd, 10000) + cooldown * (multiplier - 1)); - player.socket.emit("skill_timeout", { - name: name, - ms: min(penalty_cd, 10000) + cooldown * multiplier, - penalty: min(penalty_cd, 10000), - }); - // player.socket.emit("eval",{code:"skill_timeout('"+name+"',"+(min(penalty_cd,10000)+cooldown*multiplier)+")"}); + var penalty_cd = (player.s.penalty_cd && player.s.penalty_cd.ms) || 0; + var multiplier = 1; + //if(name=="attack" || G.skills[name].share=="attack") + if (G.skills[name].share) { + multiplier = G.skills[name].cooldown_multiplier || 1; + name = G.skills[name].share; + } + var cooldown = G.skills[name].cooldown; + if (reuse) { + cooldown = G.skills[name].reuse_cooldown; + } + if (!cooldown) { + return; + } + player.last[name] = future_ms(min(penalty_cd, 10000) + cooldown * (multiplier - 1)); + player.socket.emit("skill_timeout", { + name: name, + ms: min(penalty_cd, 10000) + cooldown * multiplier, + penalty: min(penalty_cd, 10000), + }); + // player.socket.emit("eval",{code:"skill_timeout('"+name+"',"+(min(penalty_cd,10000)+cooldown*multiplier)+")"}); } function get_entity(name) { - if (players[name_to_id[name]]) { - return players[name_to_id[name]]; - } - var l = []; - for (var iname in instances) { - if (instances[iname].monsters[name]) { - return instances[iname].monsters[name]; - } - } - return null; + if (players[name_to_id[name]]) { + return players[name_to_id[name]]; + } + var l = []; + for (var iname in instances) { + if (instances[iname].monsters[name]) { + return instances[iname].monsters[name]; + } + } + return null; } function get_player(name) { - return players[name_to_id[name]]; + return players[name_to_id[name]]; } function realm_broadcast(event, data) { - data.sname = region + " " + server_name; - appengine_call("broadcast", { event: event, data: JSON.stringify(data) }); - // broadcast(event,data); + data.sname = region + " " + server_name; + appengine_call("broadcast", { event: event, data: JSON.stringify(data) }); + // broadcast(event,data); } function broadcast(event, data) { - io.emit(event, data); - if (event == "notice" || (event == "server_message" && gameplay == "normal")) { - var to_log = false; - if (data.sname && data.sname != region + " " + server_name) { - data.message += " [" + data.sname + "]"; - } - if (event == "notice") { - data.color = "orange"; - } - if ( - event == "server_message" && - !data.event && - !data.nod && - (!data.sname || data.sname == region + " " + server_name) - ) { - var message = data.message; - if (data.sname) { - message += " [" + region + " " + server_name + "]"; - } - if (data.discord == "orange") { - message = "```css\n[" + message + "```"; - } // no ] - if (data.discord == "red") { - message = "```diff\n-" + message + "```"; - } - discord_call(message); - to_log = true; - } - if (to_log) { - appengine_call( - "server_event", - { - event: event, - keyword: variables.keyword, - id: server_id, - message: data.message, - color: data.color || "", - }, - function (result) { - server_log("Server notice: " + data.message); - }, - ); - } - } + io.emit(event, data); + if (event == "notice" || (event == "server_message" && gameplay == "normal")) { + var to_log = false; + if (data.sname && data.sname != region + " " + server_name) { + data.message += " [" + data.sname + "]"; + } + if (event == "notice") { + data.color = "orange"; + } + if ( + event == "server_message" && + !data.event && + !data.nod && + (!data.sname || data.sname == region + " " + server_name) + ) { + var message = data.message; + if (data.sname) { + message += " [" + region + " " + server_name + "]"; + } + if (data.discord == "orange") { + message = "```css\n[" + message + "```"; + } // no ] + if (data.discord == "red") { + message = "```diff\n-" + message + "```"; + } + discord_call(message); + to_log = true; + } + if (to_log) { + appengine_call( + "server_event", + { + event: event, + keyword: variables.keyword, + id: server_id, + message: data.message, + color: data.color || "", + }, + function (result) { + server_log("Server notice: " + data.message); + }, + ); + } + } } function instance_emit(name, event, data) { - var instance = instances[name] || name; - if (gameplay == "hardcore" && event != "tavern" && event != "dice") { - return broadcast(event, data); - } - for (var id in instance.players) { - var player = instance.players[id]; - if (!player.npc) { - player.socket.emit(event, data); - } - } + var instance = instances[name] || name; + if (gameplay == "hardcore" && event != "tavern" && event != "dice") { + return broadcast(event, data); + } + for (var id in instance.players) { + var player = instance.players[id]; + if (!player.npc) { + player.socket.emit(event, data); + } + } } function party_emit(name, event, data, args) { - if (!parties[name]) { - return; - } - // console.log(parties[name]); - var owners = []; - var owner = get_player(data.owner); - parties[name].forEach(function (player_name) { - var player = players[name_to_id[player_name]]; - if (args && args.instance && player.in != args.instance) { - return; - } - if (0 && in_arr(event, ["disappearing_text"])) { - player.socket.emit(event, data); - } //volatile. - else { - player.socket.emit(event, data); - } - if (event == "partym") { - owners[player.owner] = owners[player.owner] || []; - owners[player.owner].push(player.name); - } - }); - if (event == "partym" && owner) { - appengine_call("log_chat", { - to: Object.entries(owners), - type: "party", - message: data.message, - fro: owner.name, - author: owner.owner, - }); - } + if (!parties[name]) { + return; + } + // console.log(parties[name]); + var owners = []; + var owner = get_player(data.owner); + parties[name].forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (args && args.instance && player.in != args.instance) { + return; + } + if (0 && in_arr(event, ["disappearing_text"])) { + player.socket.emit(event, data); + } //volatile. + else { + player.socket.emit(event, data); + } + if (event == "partym") { + owners[player.owner] = owners[player.owner] || []; + owners[player.owner].push(player.name); + } + }); + if (event == "partym" && owner) { + appengine_call("log_chat", { + to: Object.entries(owners), + type: "party", + message: data.message, + fro: owner.name, + author: owner.owner, + }); + } } function leave_party(name, leaver) { - // [10/07/18]: For a long time chased oddities around party routines that didn't make sense, yesterday I realised that .emit causes a "disconnect" to be handled right inside this function, rather than afterwards, causing the oddities and irregularities - so - after a .emit, there's no guarantee that the player will still be there - var newparty = []; - var oldparty = parties[name]; - if (!oldparty) { - leaver.party = null; - console.log("#X NO PARTY " + name); - return; - } // Don't know the cause, maybe a disconnect triggering on the accept routines? [12/07/18] - parties[name].forEach(function (player_name) { - var player = players[name_to_id[player_name]]; - if (!player) { - console.log("#X SHOULD'VE FOUND " + player_name); - return; - } - player.party = null; - if (leaver.name != player.name) { - newparty.push(player_name); - } - }); - delete parties[name]; - if (newparty.length >= 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; - } - } - } 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"); - }); + // [10/07/18]: For a long time chased oddities around party routines that didn't make sense, yesterday I realised that .emit causes a "disconnect" to be handled right inside this function, rather than afterwards, causing the oddities and irregularities - so - after a .emit, there's no guarantee that the player will still be there + var newparty = []; + var oldparty = parties[name]; + if (!oldparty) { + leaver.party = null; + console.log("#X NO PARTY " + name); + return; + } // Don't know the cause, maybe a disconnect triggering on the accept routines? [12/07/18] + parties[name].forEach(function (player_name) { + var player = players[name_to_id[player_name]]; + if (!player) { + console.log("#X SHOULD'VE FOUND " + player_name); + return; + } + player.party = null; + if (leaver.name != player.name) { + newparty.push(player_name); + } + }); + delete parties[name]; + if (newparty.length >= 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; + } + } + } 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"); + }); } function delete_observer(socket) { - var observer = observers[socket.id]; - delete observers[socket.id]; - delete instances[observer.in].observers[observer.id]; + 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 }; - 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); + 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; - var y = entity.y; - var 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] < x && - x < player.x + player.vision[0] && - player.y - player.vision[1] < y && - y < player.y + player.vision[1]) || - player.id == must - ) { - if (event == "light" && (player.type == "rogue" || player.s.invis) && distance(player, entity) < 300) { - player.last.invis = new Date(); - delete player.s.invis; - player.socket.emit("light", { name: data.name, affected: 1 }); - resend(player, "u"); - } else if ((event == "upgrade" || event == "ui") && entity.name != player.name) { - player.socket.emit(event, data); - } //volatile. [02/02/18] - else { - player.socket.emit(event, data); - if (event == "chat_log" && data.p) { - owners[player.owner] = owners[player.owner] || []; - owners[player.owner].push(player.name); - } - } - // else if(!data.nv && in_arr(event,["disappearing_text","upgrade"])) player.socket.emit(event,data); //volatile. - } - } - for (var id in instances[entity.in].observers) { - var player = instances[entity.in].observers[id]; - if ( - player.x - player.vision[0] < x && - x < player.x + player.vision[0] && - player.y - player.vision[1] < y && - y < player.y + player.vision[1] - ) { - if (event == "upgrade") { - player.socket.emit(event, data); - } //volatile. [02/02/18] - else if (!data.nv && in_arr(event, ["disappearing_text", "upgrade"])) { - player.socket.emit(event, data); - } //volatile. - else { - player.socket.emit(event, data); - } - } - } - 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, - }); - } + var x = entity.x; + var y = entity.y; + var 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] < x && + x < player.x + player.vision[0] && + player.y - player.vision[1] < y && + y < player.y + player.vision[1]) || + player.id == must + ) { + if (event == "light" && (player.type == "rogue" || player.s.invis) && distance(player, entity) < 300) { + player.last.invis = new Date(); + delete player.s.invis; + player.socket.emit("light", { name: data.name, affected: 1 }); + resend(player, "u"); + } else if ((event == "upgrade" || event == "ui") && entity.name != player.name) { + player.socket.emit(event, data); + } //volatile. [02/02/18] + else { + player.socket.emit(event, data); + if (event == "chat_log" && data.p) { + owners[player.owner] = owners[player.owner] || []; + owners[player.owner].push(player.name); + } + } + // else if(!data.nv && in_arr(event,["disappearing_text","upgrade"])) player.socket.emit(event,data); //volatile. + } + } + for (var id in instances[entity.in].observers) { + var player = instances[entity.in].observers[id]; + if ( + player.x - player.vision[0] < x && + x < player.x + player.vision[0] && + player.y - player.vision[1] < y && + y < player.y + player.vision[1] + ) { + if (event == "upgrade") { + player.socket.emit(event, data); + } //volatile. [02/02/18] + else if (!data.nv && in_arr(event, ["disappearing_text", "upgrade"])) { + player.socket.emit(event, data); + } //volatile. + else { + player.socket.emit(event, data); + } + } + } + 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_u_logic(element) { - // sets .u so updates are sent - this element might enter someone's vision - var u = false; + // sets .u so updates are sent - this element might enter someone's vision + var u = false; - 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 (!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; - } + if (u) { + element.last_u = [element.x, element.y]; + element.u = true; + } } function xy_upush_logic(element) { - // sets .push so new entities in that area are pushed to the user/element - var u = false; + // 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 (!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]; - } + 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); - // 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)); - } - } 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 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(); - } - return args; + 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); + // 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)); + } + } 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 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(); + } + 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"; - } - request( - { - url: url, - headers: { Authorization: "Bot " + variables.discord_token }, - method: "POST", - json: { - content: message, - }, - }, - function (err, response, body) { - //console.log(response); - }, - ); + 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, + }, + }, + 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) {}, - ); - } - } + 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) {}, - ); + 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; - 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) { - 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. + 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) { + 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. } 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; + 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; - 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]); - 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 (!args) { + args = {}; + } + 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]); + 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"); + } } 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 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, + 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 }; @@ -3558,459 +3558,459 @@ var item_trade_p_ignore = { grace: true, o: true, oo: true, src: true }; // .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; - } - } 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]; - } - } - return item; + 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; + } + } 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]; + } + } + return item; } 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"]; - } - return []; + 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"]; + } + 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); - }); + 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) { - // 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]); - }); - for (var i = 0; i < player.items.length; i++) { - player.citems[i] = cache_item(player.items[i]); - } - reslot_player(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]); + }); + for (var i = 0; i < player.items.length; i++) { + player.citems[i] = cache_item(player.items[i]); + } + reslot_player(player); } function init_bank(player) { - player.cuser = {}; - for (var pack in bank_packs) { - if (!player.user[pack]) { - continue; - } - player.cuser[pack] = []; - for (var i = 0; i < player.user[pack].length; i++) { - if (!player.user[pack][i]) { - player.cuser[pack][i] = null; - continue; - } - if (player.user[pack][i].expires) { - player.user[pack][i].expires = new Date(player.user[pack][i].expires); - } - player.cuser[pack][i] = cache_item(player.user[pack][i]); - } - } + player.cuser = {}; + for (var pack in bank_packs) { + if (!player.user[pack]) { + continue; + } + player.cuser[pack] = []; + for (var i = 0; i < player.user[pack].length; i++) { + if (!player.user[pack][i]) { + player.cuser[pack][i] = null; + continue; + } + if (player.user[pack][i].expires) { + player.user[pack][i].expires = new Date(player.user[pack][i].expires); + } + player.cuser[pack][i] = cache_item(player.user[pack][i]); + } + } } 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; - } - } + 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; - } - 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; i < player.items.length; i++) { - if (!player.items[i]) { - continue; - } - delete player.items[i].m; - 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; - } - } - 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.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]) }; - } - } - - 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 + 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; + } + for (var i = 0; i < player.items.length; i++) { + if (!player.items[i]) { + continue; + } + delete player.items[i].m; + 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; + } + } + 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.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]) }; + } + } + + 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]; - } - } + // 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]; - 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) { + 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(); + } } function item_name(item) { - var def = G.items[item.name]; - var name = def.name; - if (item.level) { - name += " +" + item.level; - } - return name; + 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; - } - return false; + 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; + 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; - } + 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; - } - }); - return safe; + 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; - } - // 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; - } - }); - return safe; + 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; + } + }); + return safe; } 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); - // [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 }; - } - }); - return point; + var point = false; + // if(is_xy_safe(map,x,y)) return {x:x,y: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 }; + } + }); + 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) < abs(x - x2)) { - return x1; - } - return x2; + // 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) < abs(x - x2)) { + return x1; + } + return x2; } function phash(x, y) { - if (x.x !== undefined) { - y = x.y; - x = x.x; - } - x = parseInt(x) - (parseInt(x) % smap_step); - y = parseInt(y) - (parseInt(y) % smap_step); - return x + "|" + y; + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + return x + "|" + y; } // Without rphash, halloween -448.0000001,-158.0000001 rounds to a corner which is a 2 function rphash(x, y) { - if (x.x !== undefined) { - y = x.y; - x = x.x; - } - x = smap_round(x); - y = smap_round(y); - return x + "|" + y; + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + x = smap_round(x); + y = smap_round(y); + return x + "|" + y; } var smap_data = {}; @@ -4019,1054 +4019,1054 @@ var smap_edge = 60; // for smap_step 24, the edge was 40 - also check out access // if(is_sdk) smap_step=24; // 10 takes toooo long [22/06/18] var hiding_places = []; function server_bfs(map) { - if ((precomputed && precomputed.version == G.version) || (precomputed && precomputed.smap_data)) { - smap_data[map] = precomputed.smap_data[map]; - amap_data[map] = precomputed.amap_data[map]; - return; - } - if (G.maps[map].no_bounds) { - smap_data[map] = -1; - amap_data[map] = {}; - return; - } - if (is_sdk && variables.fast_sdk && !(map == "level1" || map == "arena")) { - smap_data[map] = -1; - amap_data[map] = {}; - return; - } - server_bfs2(map); - smap_data[map] = {}; - var queue = []; - var visited = {}; - var start = 0; - var level = 0; - var chiding_places = []; - function push(x, y, c) { - x = parseInt(x) - (parseInt(x) % smap_step); - y = parseInt(y) - (parseInt(y) % smap_step); - var hash = phash(x, y); - if (visited[hash] && visited[hash].l <= c) { - return; - } - if (!c && !G.maps[map].pvp && !G.maps[map].instance && Math.random() < 0.01) { - chiding_places.push([map, x, y]); - } - if (c == level) { - queue.push([x, y]); - } - visited[hash] = { l: c, x: x, y: y }; - smap_data[map][hash] = c; - } - for (level = 0; level < 7; level++) { - if (level == 0) { - G.maps[map].spawns.forEach(function (s) { - var x = s[0]; - var y = s[1]; - var done = false; - x = parseInt(x) - (parseInt(x) % smap_step); - y = parseInt(y) - (parseInt(y) % smap_step); - var current = [x, y]; - [ - [0, 0], - [0, smap_step], - [0, -smap_step], - [smap_step, 0], - [-smap_step, 0], - ].forEach(function (m) { - if (!done && can_move({ map: map, x: x, y: y, going_x: current[0] + m[0], going_y: current[1] + m[1] })) { - push(current[0] + m[0], current[1] + m[1], level); - done = true; - } - }); - }); - } else { - for (var h in visited) { - var e = visited[h]; - if (e.l == level) { - queue.push([e.x, e.y]); - } - } - } - 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); - } - }); - } - 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]; j < current[2]; j += smap_step) { - push(current[0], j, 0); - [ - [0, smap_step], - [0, -smap_step], - [smap_step, 0], - [-smap_step, 0], - ].forEach(function (m) { - push(current[0] + m[0], j + m[1], 1); - }); - } - } - for (var i = 0; i < (G.maps[map].data.y_lines || []).length; i++) { - var current = G.maps[map].data.y_lines[i]; - push(current[1], current[0], 0); - push(current[2], current[0], 0); - for (var j = current[1]; j < current[2]; j += smap_step) { - push(j, current[0], 0); - [ - [0, smap_step], - [0, -smap_step], - [smap_step, 0], - [-smap_step, 0], - ].forEach(function (m) { - push(j + m[0], current[0] + m[1], 1); - }); - } - } - } - start = 0; - queue = []; - } - if (chiding_places.length) { - Array.prototype.push.apply(hiding_places, chiding_places); - } + if ((precomputed && precomputed.version == G.version) || (precomputed && precomputed.smap_data)) { + smap_data[map] = precomputed.smap_data[map]; + amap_data[map] = precomputed.amap_data[map]; + return; + } + if (G.maps[map].no_bounds) { + smap_data[map] = -1; + amap_data[map] = {}; + return; + } + if (is_sdk && variables.fast_sdk && !(map == "level1" || map == "arena")) { + smap_data[map] = -1; + amap_data[map] = {}; + return; + } + server_bfs2(map); + smap_data[map] = {}; + var queue = []; + var visited = {}; + var start = 0; + var level = 0; + var chiding_places = []; + function push(x, y, c) { + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + var hash = phash(x, y); + if (visited[hash] && visited[hash].l <= c) { + return; + } + if (!c && !G.maps[map].pvp && !G.maps[map].instance && Math.random() < 0.01) { + chiding_places.push([map, x, y]); + } + if (c == level) { + queue.push([x, y]); + } + visited[hash] = { l: c, x: x, y: y }; + smap_data[map][hash] = c; + } + for (level = 0; level < 7; level++) { + if (level == 0) { + G.maps[map].spawns.forEach(function (s) { + var x = s[0]; + var y = s[1]; + var done = false; + x = parseInt(x) - (parseInt(x) % smap_step); + y = parseInt(y) - (parseInt(y) % smap_step); + var current = [x, y]; + [ + [0, 0], + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + if (!done && can_move({ map: map, x: x, y: y, going_x: current[0] + m[0], going_y: current[1] + m[1] })) { + push(current[0] + m[0], current[1] + m[1], level); + done = true; + } + }); + }); + } else { + for (var h in visited) { + var e = visited[h]; + if (e.l == level) { + queue.push([e.x, e.y]); + } + } + } + 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); + } + }); + } + 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]; j < current[2]; j += smap_step) { + push(current[0], j, 0); + [ + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + push(current[0] + m[0], j + m[1], 1); + }); + } + } + for (var i = 0; i < (G.maps[map].data.y_lines || []).length; i++) { + var current = G.maps[map].data.y_lines[i]; + push(current[1], current[0], 0); + push(current[2], current[0], 0); + for (var j = current[1]; j < current[2]; j += smap_step) { + push(j, current[0], 0); + [ + [0, smap_step], + [0, -smap_step], + [smap_step, 0], + [-smap_step, 0], + ].forEach(function (m) { + push(j + m[0], current[0] + m[1], 1); + }); + } + } + } + start = 0; + queue = []; + } + if (chiding_places.length) { + Array.prototype.push.apply(hiding_places, chiding_places); + } } function amap_round(x) { - // for blink, port, magiport - var x1 = parseInt(x) - (parseInt(x) % amap_step); - var x2 = x1 + amap_step; - if (x < 0) { - x2 = x1 - amap_step; - } - if (abs(x - x1) < abs(x - x2)) { - return x1; - } - return x2; + // for blink, port, magiport + var x1 = parseInt(x) - (parseInt(x) % amap_step); + var x2 = x1 + amap_step; + if (x < 0) { + x2 = x1 - amap_step; + } + if (abs(x - x1) < abs(x - x2)) { + return x1; + } + return x2; } function phash2(x, y) { - if (x.x !== undefined) { - y = x.y; - x = x.x; - } - x = parseInt(x) - (parseInt(x) % amap_step); - y = parseInt(y) - (parseInt(y) % amap_step); - return x + "|" + y; + if (x.x !== undefined) { + y = x.y; + x = x.x; + } + 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; + 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; - } - 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; - } - }); - 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; - } - }); - amap_data[map][hash] = count; - } + 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; + } + 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; + } + }); + 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; + } + }); + 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 = (tx < sx && -1) || 1; - var step = 8; - for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { - if (!amap_data[map][amap_round(sx + p * s * step) + "|" + amap_round(ty)]) { - return false; - } - } - } else if (sx == tx) { - var p = (ty < sy && -1) || 1; - var step = 8; - for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { - if (!amap_data[map][amap_round(tx) + "|" + amap_round(sy + p * s * step)]) { - return false; - } - } - } else { - var len = point_distance(sx, sy, tx, ty); - var p = (tx < sx && -1) || 1; - var step = 5; - for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { - var x = sx + p * s * step; - var y = sy + ((ty - sy) * abs(s * step)) / abs(tx - sx); - //console.log("x-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); - if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { - return false; - } - } - var p = (ty < sy && -1) || 1; - var step = 5; - for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { - var x = sx + ((tx - sx) * abs(s * step)) / abs(ty - sy); - var y = sy + p * s * step; - //console.log("y-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); - if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { - return false; - } - } - } - return true; + if (!amap_data[map][amap_round(tx) + "|" + amap_round(ty)]) { + return false; + } + + if (sy == ty) { + var p = (tx < sx && -1) || 1; + var step = 8; + for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { + if (!amap_data[map][amap_round(sx + p * s * step) + "|" + amap_round(ty)]) { + return false; + } + } + } else if (sx == tx) { + var p = (ty < sy && -1) || 1; + var step = 8; + for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { + if (!amap_data[map][amap_round(tx) + "|" + amap_round(sy + p * s * step)]) { + return false; + } + } + } else { + var len = point_distance(sx, sy, tx, ty); + var p = (tx < sx && -1) || 1; + var step = 5; + for (var s = 1; s < parseInt(abs((tx - sx) / step)); s++) { + var x = sx + p * s * step; + var y = sy + ((ty - sy) * abs(s * step)) / abs(tx - sx); + //console.log("x-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); + if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { + return false; + } + } + var p = (ty < sy && -1) || 1; + var step = 5; + for (var s = 1; s < parseInt(abs((ty - sy) / step)); s++) { + var x = sx + ((tx - sx) * abs(s * step)) / abs(ty - sy); + var y = sy + p * s * step; + //console.log("y-checks: "+to_pretty_float(x)+","+to_pretty_float(y)); + if (!amap_data[map][amap_round(x) + "|" + amap_round(y)]) { + return false; + } + } + } + return true; } function random_place(map) { - var place = random_one(Object.keys(amap_data[map] || {})); - if (!place) { - return null; - } - var xy = place.split("|"); - return { x: parseInt(xy[0]), y: parseInt(xy[1]), map: map, in: map }; + var place = random_one(Object.keys(amap_data[map] || {})); + if (!place) { + return null; + } + var xy = place.split("|"); + return { x: parseInt(xy[0]), y: parseInt(xy[1]), map: map, in: map }; } function fast_astar(args) { - var map = args.map; - var sx = args.sx; - var sy = args.sy; - var tx = args.tx; - var ty = args.ty; - var heap = vHeap(); - var visited = {}; - var total = 0; - var start = new Date(); - var best = 999999999999; - var theone = null; - var good = false; - function hpush(cx, cy, fr, dir, bad) { - // if(visited[cx+"|"+cy]) return; - var value = point_distance(cx, cy, tx, ty); - var hash = cx + "|" + cy; - if (amap_data[map][hash] < 4) { - bad += 2; - } else if (amap_data[map][hash] < 6) { - bad += 1; - } else if (amap_data[map][hash] < 8) { - bad += 0.5; - } - total += 1; - // console.log(["hpush",total,cx,cy,bad,value]); - heap.insert({ value: value + bad * 0.25, x: cx, y: cy, bad: bad, dir: dir }); - visited[hash] = fr; - } - function finalise(current) { - var dx = current.x; - var dy = current.y; - path = [[dx, dy]]; - while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { - dx = visited[dx + "|" + dy].split("|"); - dy = parseInt(dx[1]); - dx = parseInt(dx[0]); - path.push([dx, dy]); - } - path.reverse(); - // path.push([tx,ty]); // Looks bad - dx = path[0][0]; - dy = path[0][1]; - // console.log(path); - for ( - var i = 1; - i < path.length; - i++ // 1ms at max [17/08/20] - ) { - if (can_amove(map, sx, sy, path[i][0], path[i][1])) { - dx = path[i][0]; - dy = path[i][1]; - } else { - break; - } - } - // if(!good && point_distance(sx,sy,dx,dy)<30) return null; - server_log([total, dx, dy, mssince(start)]); - return [dx, dy]; - } - for (var step = 1; step <= 2; step++) { - if (heap.array.length) { - break; - } - [ - [0, 0, 0, 0], - [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 = amap_round(sx + m[0] * step); - var cy = amap_round(sy + m[1] * step); - if (amap_data[map][cx + "|" + cy]) { - hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); - } - }); - } - 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(); - 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; - 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_astar"]); + var map = args.map; + var sx = args.sx; + var sy = args.sy; + var tx = args.tx; + var ty = args.ty; + var heap = vHeap(); + var visited = {}; + var total = 0; + var start = new Date(); + var best = 999999999999; + var theone = null; + var good = false; + function hpush(cx, cy, fr, dir, bad) { + // if(visited[cx+"|"+cy]) return; + var value = point_distance(cx, cy, tx, ty); + var hash = cx + "|" + cy; + if (amap_data[map][hash] < 4) { + bad += 2; + } else if (amap_data[map][hash] < 6) { + bad += 1; + } else if (amap_data[map][hash] < 8) { + bad += 0.5; + } + total += 1; + // console.log(["hpush",total,cx,cy,bad,value]); + heap.insert({ value: value + bad * 0.25, x: cx, y: cy, bad: bad, dir: dir }); + visited[hash] = fr; + } + function finalise(current) { + var dx = current.x; + var dy = current.y; + path = [[dx, dy]]; + while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { + dx = visited[dx + "|" + dy].split("|"); + dy = parseInt(dx[1]); + dx = parseInt(dx[0]); + path.push([dx, dy]); + } + path.reverse(); + // path.push([tx,ty]); // Looks bad + dx = path[0][0]; + dy = path[0][1]; + // console.log(path); + for ( + var i = 1; + i < path.length; + i++ // 1ms at max [17/08/20] + ) { + if (can_amove(map, sx, sy, path[i][0], path[i][1])) { + dx = path[i][0]; + dy = path[i][1]; + } else { + break; + } + } + // if(!good && point_distance(sx,sy,dx,dy)<30) return null; + server_log([total, dx, dy, mssince(start)]); + return [dx, dy]; + } + for (var step = 1; step <= 2; step++) { + if (heap.array.length) { + break; + } + [ + [0, 0, 0, 0], + [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 = amap_round(sx + m[0] * step); + var cy = amap_round(sy + m[1] * step); + if (amap_data[map][cx + "|" + cy]) { + hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); + } + }); + } + 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(); + 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; + 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_astar"]); } function fast_abfs(monster, tx, ty) { - var map = monster.map; - var sx = monster.x; - var sy = monster.y; - var start = 0; - var last = 0; - var queue = []; - var visited = {}; - var start_t = new Date(); - var best = 999999999999; - var theone = null; - var good = false; - function hpush(cx, cy, fr, dir, bad) { - var value = point_distance(cx, cy, tx, ty); - var hash = cx + "|" + cy; - queue[last++] = { x: cx, y: cy, bad: bad, dir: dir }; - visited[hash] = fr; - } - function finalise(current) { - var dx = current.x; - var dy = current.y; - path = [[dx, dy]]; - while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { - dx = visited[dx + "|" + dy].split("|"); - dy = parseInt(dx[1]); - dx = parseInt(dx[0]); - path.push([dx, dy]); - } - path.reverse(); - // path.push([tx,ty]); // Looks bad - dx = path[0][0]; - dy = path[0][1]; - for ( - var i = 1; - i < path.length; - i++ // 1ms at max [17/08/20] - ) { - if (can_amove(map, sx, sy, path[i][0], path[i][1])) { - dx = path[i][0]; - dy = path[i][1]; - } else { - break; - } - } - if (!good && point_distance(sx, sy, dx, dy) < 30) { - return null; - } - server_log([last, dx, dy, mssince(start_t)]); - return [dx, dy]; - } - for (var step = 1; step <= 2; step++) { - if (last) { - break; - } - [ - [sx, sy, 0, 0], - [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 = amap_round(sx + m[0] * step); - var cy = amap_round(sy + m[1] * step); - if (amap_data[map][cx + "|" + cy]) { - hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); - } - }); - } - while (start < last) { - if (last - start > 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"]); + var map = monster.map; + var sx = monster.x; + var sy = monster.y; + var start = 0; + var last = 0; + var queue = []; + var visited = {}; + var start_t = new Date(); + var best = 999999999999; + var theone = null; + var good = false; + function hpush(cx, cy, fr, dir, bad) { + var value = point_distance(cx, cy, tx, ty); + var hash = cx + "|" + cy; + queue[last++] = { x: cx, y: cy, bad: bad, dir: dir }; + visited[hash] = fr; + } + function finalise(current) { + var dx = current.x; + var dy = current.y; + path = [[dx, dy]]; + while (visited[dx + "|" + dy] && visited[dx + "|" + dy] != "start") { + dx = visited[dx + "|" + dy].split("|"); + dy = parseInt(dx[1]); + dx = parseInt(dx[0]); + path.push([dx, dy]); + } + path.reverse(); + // path.push([tx,ty]); // Looks bad + dx = path[0][0]; + dy = path[0][1]; + for ( + var i = 1; + i < path.length; + i++ // 1ms at max [17/08/20] + ) { + if (can_amove(map, sx, sy, path[i][0], path[i][1])) { + dx = path[i][0]; + dy = path[i][1]; + } else { + break; + } + } + if (!good && point_distance(sx, sy, dx, dy) < 30) { + return null; + } + server_log([last, dx, dy, mssince(start_t)]); + return [dx, dy]; + } + for (var step = 1; step <= 2; step++) { + if (last) { + break; + } + [ + [sx, sy, 0, 0], + [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 = amap_round(sx + m[0] * step); + var cy = amap_round(sy + m[1] * step); + if (amap_data[map][cx + "|" + cy]) { + hpush(cx, cy, "start", m[2], point_distance(sx, sy, cx, cy)); + } + }); + } + while (start < last) { + if (last - start > 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; - } - - 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]); - } + 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] + + 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) { + 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(); + } + } } function get_call_cost(socket) { - if (!socket) { - socket = current_socket; - } - if (socket.socket) { - socket = socket.socket; - } // player + 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] + 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; + var cost = 0; + socket.calls.forEach(function (c) { + cost += c[2]; + }); + return cost; } function set_direction() {} // compatibility function symmetricDecrypt(input, key, checkHmac) { - 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); - aesData.end(input.slice(16)); - var plaintext = aesData.read(); - - if (checkHmac) { - // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC - var remotePartialHmac = iv.slice(0, iv.length - 3); - var random = iv.slice(iv.length - 3, iv.length); - var hmac = crypto.createHmac("sha1", key.slice(0, 16)); - hmac.update(random); - hmac.update(plaintext); - if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { - throw new Error("Received invalid HMAC from remote host."); - } - } - - return plaintext; + 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); + aesData.end(input.slice(16)); + var plaintext = aesData.read(); + + if (checkHmac) { + // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC + var remotePartialHmac = iv.slice(0, iv.length - 3); + var random = iv.slice(iv.length - 3, iv.length); + var hmac = crypto.createHmac("sha1", key.slice(0, 16)); + hmac.update(random); + hmac.update(plaintext); + if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { + throw new Error("Received invalid HMAC from remote host."); + } + } + + return plaintext; } function parseAppTicket(ticket) { - // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl - - // console.log(ticket); - if (!ByteBuffer.isByteBuffer(ticket)) { - ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); - } - - let details = {}; - - try { - let initialLength = ticket.readUint32(); - // console.log(initialLength); - if (initialLength == 20) { - // This is a full appticket, with a GC token and session header (in addition to ownership ticket) - details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation - - details.gcToken = ticket.readUint64().toString(); - //details.steamID = new SteamID(ticket.readUint64().toString()); - ticket.skip(8); // the SteamID gets read later on - details.tokenGenerated = new Date(ticket.readUint32() * 1000); - - if (ticket.readUint32() != 24) { - // SESSIONHEADER should be 24 bytes. - return null; - } - - ticket.skip(8); // unknown 1 and unknown 2 - details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); - ticket.skip(4); // filler - details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms - details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to - - if (ticket.readUint32() + ticket.offset != ticket.limit) { - // OWNERSHIPSECTIONWITHSIGNATURE sectlength - return null; - } - } else { - ticket.skip(-4); - } - - // 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 - ) { - return null; - } - - let i; - let j; - let dlc; - - details.version = ticket.readUint32(); - details.steamID = ticket.readUint64().toString(); - details.appID = ticket.readUint32(); - details.ownershipTicketExternalIP = ticket.readUint32(); - details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( - details.ownershipFlags = ticket.readUint32(); - details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); - details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); - details.licenses = []; - // return details; - - let licenseCount = ticket.readUint16(); - for (i = 0; i < licenseCount; i++) { - details.licenses.push(ticket.readUint32()); - } - - details.dlc = []; - - let dlcCount = ticket.readUint16(); - for (i = 0; i < dlcCount; i++) { - dlc = {}; - dlc.appID = ticket.readUint32(); - dlc.licenses = []; - - licenseCount = ticket.readUint16(); - - for (j = 0; j < licenseCount; j++) { - dlc.licenses.push(ticket.readUint32()); - } - - details.dlc.push(dlc); - } - - ticket.readUint16(); // reserved - if (ticket.offset + 128 == ticket.limit) { - // Has signature - details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); - } - - let date = new Date(); - details.isExpired = details.ownershipTicketExpires < date; - 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); - return details; - return null; // not a valid ticket - } - - return details; + // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl + + // console.log(ticket); + if (!ByteBuffer.isByteBuffer(ticket)) { + ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); + } + + let details = {}; + + try { + let initialLength = ticket.readUint32(); + // console.log(initialLength); + if (initialLength == 20) { + // This is a full appticket, with a GC token and session header (in addition to ownership ticket) + details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation + + details.gcToken = ticket.readUint64().toString(); + //details.steamID = new SteamID(ticket.readUint64().toString()); + ticket.skip(8); // the SteamID gets read later on + details.tokenGenerated = new Date(ticket.readUint32() * 1000); + + if (ticket.readUint32() != 24) { + // SESSIONHEADER should be 24 bytes. + return null; + } + + ticket.skip(8); // unknown 1 and unknown 2 + details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); + ticket.skip(4); // filler + details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms + details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to + + if (ticket.readUint32() + ticket.offset != ticket.limit) { + // OWNERSHIPSECTIONWITHSIGNATURE sectlength + return null; + } + } else { + ticket.skip(-4); + } + + // 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 + ) { + return null; + } + + let i; + let j; + let dlc; + + details.version = ticket.readUint32(); + details.steamID = ticket.readUint64().toString(); + details.appID = ticket.readUint32(); + details.ownershipTicketExternalIP = ticket.readUint32(); + details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( + details.ownershipFlags = ticket.readUint32(); + details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); + details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); + details.licenses = []; + // return details; + + let licenseCount = ticket.readUint16(); + for (i = 0; i < licenseCount; i++) { + details.licenses.push(ticket.readUint32()); + } + + details.dlc = []; + + let dlcCount = ticket.readUint16(); + for (i = 0; i < dlcCount; i++) { + dlc = {}; + dlc.appID = ticket.readUint32(); + dlc.licenses = []; + + licenseCount = ticket.readUint16(); + + for (j = 0; j < licenseCount; j++) { + dlc.licenses.push(ticket.readUint32()); + } + + details.dlc.push(dlc); + } + + ticket.readUint16(); // reserved + if (ticket.offset + 128 == ticket.limit) { + // Has signature + details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); + } + + let date = new Date(); + details.isExpired = details.ownershipTicketExpires < date; + 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); + 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 }, - }, - }, - }, + 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 - // 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] - - // the ipAddress we return - var ipAddress; - - // 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"]; - - // 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"]; - - // 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 reqInfoRemoteAddress = req.info ? req.info.remoteAddress : null; - - // x-client-ip - if (clientIp) { - ipAddress = clientIp; - } - - // 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 - // 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(","); - ipAddress = forwardedIps[0]; - } - - // 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 - // (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 - else if (clusterClientIp) { - ipAddress = clusterClientIp; - } - - // x-forwarded - else if (forwardedAlt) { - ipAddress = forwardedAlt; - } - - // forwarded-for - else if (forwardedFor) { - ipAddress = forwardedFor; - } - - // forwarded - else if (forwarded) { - ipAddress = forwarded; - } - - // remote address checks - else if (reqConnectionRemoteAddress) { - ipAddress = reqConnectionRemoteAddress; - } 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; + //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] + + // the ipAddress we return + var ipAddress; + + // 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"]; + + // 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"]; + + // 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 reqInfoRemoteAddress = req.info ? req.info.remoteAddress : null; + + // x-client-ip + if (clientIp) { + ipAddress = clientIp; + } + + // 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 + // 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(","); + ipAddress = forwardedIps[0]; + } + + // 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 + // (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 + else if (clusterClientIp) { + ipAddress = clusterClientIp; + } + + // x-forwarded + else if (forwardedAlt) { + ipAddress = forwardedAlt; + } + + // forwarded-for + else if (forwardedFor) { + ipAddress = forwardedFor; + } + + // forwarded + else if (forwarded) { + ipAddress = forwarded; + } + + // remote address checks + else if (reqConnectionRemoteAddress) { + ipAddress = reqConnectionRemoteAddress; + } 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"; - } - return "male"; + 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; - } - } - } - } - } - } - } - } - } - } - } + 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; + } + } + } + } + } + } + } + } + } + } + } } /* 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 + 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 } /* eslint-enable no-misleading-character-class,no-control-regex */ @@ -5074,259 +5074,259 @@ function strip_string(str) { 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 }); + 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 }); } // #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 }], - ]; - 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) { - 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 }), - }); - } - } + 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, + }); + } + }); + 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 }), + }); + } + } } function add_achievement(player, name) { - player.p.achievements[name] = (player.p.achievements[name] || 0) + 1; - player.socket.emit("achievement_success", { name: 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; - var old = item.acc; - var rr = G.achievements[ach].rr || 1; - item.ach = ach; - item.acc += amount || 1; - if (item.acc < needed) { - if (parseInt(old / rr) != parseInt(item.acc / rr)) { - player.socket.emit("achievement_progress", { name: ach, count: item.acc, needed: needed }); - } - } else { - add_item_property(item, G.achievements[ach].title); - delete item.ach; - delete item.acc; - add_achievement(player, ach); - cache_player_items(player); - resend(player, "u+cid+reopen"); - } + if (!G.achievements[ach] || !item) { + return; + } + if (item.ach != ach || !item.acc) { + item.acc = 0; + } + var needed = G.achievements[ach].count; + var old = item.acc; + var rr = G.achievements[ach].rr || 1; + item.ach = ach; + item.acc += amount || 1; + if (item.acc < needed) { + if (parseInt(old / rr) != parseInt(item.acc / rr)) { + player.socket.emit("achievement_progress", { name: ach, count: item.acc, needed: needed }); + } + } else { + add_item_property(item, G.achievements[ach].title); + delete item.ach; + delete item.acc; + add_achievement(player, ach); + cache_player_items(player); + resend(player, "u+cid+reopen"); + } } 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); - } + try { + if (monster.type == "grinch") { + item_achievement_increment(player, player.slots.cape, "festive", damage); + } + } catch (e) { + console.log("#A: " + e); + } } 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); - } + 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); + } } 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); - } + 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_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); - } + 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 }); - } - } else { - } - } catch (e) { - console.log("#A: " + e); - } + 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); + } } 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); - } + try { + if (server.shutdown) { + return; + } + if (player.slots.mainhand) { + delete player.slots.mainhand.ach; + } + } 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; - } + 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); - } + 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); - } + 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 302dc8ba..69d837f7 100644 --- a/node/server_worker.js +++ b/node/server_worker.js @@ -12,17 +12,17 @@ var smap_data = workerData.smap_data; var amap_data = workerData.amap_data; 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) { - console.log(e); - } + 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) { + console.log(e); + } }); setInterval(function () {}, 10); diff --git a/node/test.js b/node/test.js index 4bdad915..363e6922 100644 --- a/node/test.js +++ b/node/test.js @@ -3,174 +3,174 @@ var protobuf = require("protobufjs"); var ByteBuffer = require("bytebuffer"); function symmetricDecrypt(input, key, checkHmac) { - 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); - aesData.end(input.slice(16)); - var plaintext = aesData.read(); - - if (checkHmac) { - // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC - var remotePartialHmac = iv.slice(0, iv.length - 3); - var random = iv.slice(iv.length - 3, iv.length); - var hmac = crypto.createHmac("sha1", key.slice(0, 16)); - hmac.update(random); - hmac.update(plaintext); - if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { - throw new Error("Received invalid HMAC from remote host."); - } - } - - return plaintext; + 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); + aesData.end(input.slice(16)); + var plaintext = aesData.read(); + + if (checkHmac) { + // The last 3 bytes of the IV are a random value, and the remainder are a partial HMAC + var remotePartialHmac = iv.slice(0, iv.length - 3); + var random = iv.slice(iv.length - 3, iv.length); + var hmac = crypto.createHmac("sha1", key.slice(0, 16)); + hmac.update(random); + hmac.update(plaintext); + if (!remotePartialHmac.equals(hmac.digest().slice(0, remotePartialHmac.length))) { + throw new Error("Received invalid HMAC from remote host."); + } + } + + return plaintext; } function parseAppTicket(ticket) { - // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl - - console.log(ticket); - if (!ByteBuffer.isByteBuffer(ticket)) { - ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); - } - - let details = {}; - - try { - let initialLength = ticket.readUint32(); - console.log(initialLength); - if (initialLength == 20) { - // This is a full appticket, with a GC token and session header (in addition to ownership ticket) - details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation - - details.gcToken = ticket.readUint64().toString(); - //details.steamID = new SteamID(ticket.readUint64().toString()); - ticket.skip(8); // the SteamID gets read later on - details.tokenGenerated = new Date(ticket.readUint32() * 1000); - - if (ticket.readUint32() != 24) { - // SESSIONHEADER should be 24 bytes. - return null; - } - - ticket.skip(8); // unknown 1 and unknown 2 - details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); - ticket.skip(4); // filler - details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms - details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to - - if (ticket.readUint32() + ticket.offset != ticket.limit) { - // OWNERSHIPSECTIONWITHSIGNATURE sectlength - return null; - } - } else { - ticket.skip(-4); - } - - // 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 - ) { - return null; - } - - let i; - let j; - let dlc; - - details.version = ticket.readUint32(); - details.steamID = ticket.readUint64().toString(); - details.appID = ticket.readUint32(); - details.ownershipTicketExternalIP = ticket.readUint32(); - details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( - details.ownershipFlags = ticket.readUint32(); - details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); - details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); - details.licenses = []; - // return details; - - let licenseCount = ticket.readUint16(); - for (i = 0; i < licenseCount; i++) { - details.licenses.push(ticket.readUint32()); - } - - details.dlc = []; - - let dlcCount = ticket.readUint16(); - for (i = 0; i < dlcCount; i++) { - dlc = {}; - dlc.appID = ticket.readUint32(); - dlc.licenses = []; - - licenseCount = ticket.readUint16(); - - for (j = 0; j < licenseCount; j++) { - dlc.licenses.push(ticket.readUint32()); - } - - details.dlc.push(dlc); - } - - ticket.readUint16(); // reserved - if (ticket.offset + 128 == ticket.limit) { - // Has signature - details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); - } - - let date = new Date(); - details.isExpired = details.ownershipTicketExpires < date; - 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); - return details; - return null; // not a valid ticket - } - - return details; + // https://github.com/SteamRE/SteamKit/blob/master/Resources/Structs/steam3_appticket.hsl + + console.log(ticket); + if (!ByteBuffer.isByteBuffer(ticket)) { + ticket = ByteBuffer.wrap(ticket, ByteBuffer.LITTLE_ENDIAN); + } + + let details = {}; + + try { + let initialLength = ticket.readUint32(); + console.log(initialLength); + if (initialLength == 20) { + // This is a full appticket, with a GC token and session header (in addition to ownership ticket) + details.authTicket = ticket.slice(ticket.offset - 4, ticket.offset - 4 + 52).toBuffer(); // this is the part that's passed back to Steam for validation + + details.gcToken = ticket.readUint64().toString(); + //details.steamID = new SteamID(ticket.readUint64().toString()); + ticket.skip(8); // the SteamID gets read later on + details.tokenGenerated = new Date(ticket.readUint32() * 1000); + + if (ticket.readUint32() != 24) { + // SESSIONHEADER should be 24 bytes. + return null; + } + + ticket.skip(8); // unknown 1 and unknown 2 + details.sessionExternalIP = Helpers.ipIntToString(ticket.readUint32()); + ticket.skip(4); // filler + details.clientConnectionTime = ticket.readUint32(); // time the client has been connected to Steam in ms + details.clientConnectionCount = ticket.readUint32(); // how many servers the client has connected to + + if (ticket.readUint32() + ticket.offset != ticket.limit) { + // OWNERSHIPSECTIONWITHSIGNATURE sectlength + return null; + } + } else { + ticket.skip(-4); + } + + // 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 + ) { + return null; + } + + let i; + let j; + let dlc; + + details.version = ticket.readUint32(); + details.steamID = ticket.readUint64().toString(); + details.appID = ticket.readUint32(); + details.ownershipTicketExternalIP = ticket.readUint32(); + details.ownershipTicketInternalIP = ticket.readUint32(); // Helpers.ipIntToString( + details.ownershipFlags = ticket.readUint32(); + details.ownershipTicketGenerated = new Date(ticket.readUint32() * 1000); + details.ownershipTicketExpires = new Date(ticket.readUint32() * 1000); + details.licenses = []; + // return details; + + let licenseCount = ticket.readUint16(); + for (i = 0; i < licenseCount; i++) { + details.licenses.push(ticket.readUint32()); + } + + details.dlc = []; + + let dlcCount = ticket.readUint16(); + for (i = 0; i < dlcCount; i++) { + dlc = {}; + dlc.appID = ticket.readUint32(); + dlc.licenses = []; + + licenseCount = ticket.readUint16(); + + for (j = 0; j < licenseCount; j++) { + dlc.licenses.push(ticket.readUint32()); + } + + details.dlc.push(dlc); + } + + ticket.readUint16(); // reserved + if (ticket.offset + 128 == ticket.limit) { + // Has signature + details.signature = ticket.slice(ticket.offset, ticket.offset + 128).toBuffer(); + } + + let date = new Date(); + details.isExpired = details.ownershipTicketExpires < date; + 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); + 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 }, - }, - }, - }, + 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", - ), + new Buffer( + "080110a7e59890031815204f2a80014527ad8cccd8cd9b7d3260529556004ddf0454a1c2e8543495b1eb72b3dff44d9d67555798ef44b7e3f97cf86d3bf1bc3f84a91de415c382b0d96033c8e5398f2b775bcb004f5487c651d81ef410d7d2917cbb407713397d2c29a455b6852eac7db1a0e52248585bd5440ef07de54b7519f67eefbad5420d97b77ef7e35450af", + "hex", + ), ); var decrypted = symmetricDecrypt( - outer.encryptedTicket, - new Buffer("8af5c487af6c7709ef1a4e6e2275fcc0a90f57ae4319de793fca3d7ed8655520", "hex"), + 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), + decrypted.slice(outer.cbEncrypteduserdata, outer.cbEncrypteduserdata + ownershipTicketLength), ); if (ownershipTicket) { - ownershipTicket.userData = userData.toString(); + ownershipTicket.userData = userData.toString(); } console.log(ownershipTicket); From 36df153ac895a507d0beaacca89598520cc5841b Mon Sep 17 00:00:00 2001 From: Telokis Date: Sat, 28 Oct 2023 17:49:28 +0200 Subject: [PATCH 6/8] Add README entry regarding formatting --- README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 84585127..928557f3 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 From 39d5f28c9beafb284fe6ca12d9bc0c22824a9d62 Mon Sep 17 00:00:00 2001 From: FreezePhoenix Date: Sun, 5 Nov 2023 14:36:16 -0500 Subject: [PATCH 7/8] Fix one small bug lul --- node/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/server.js b/node/server.js index a5ef3fd8..6dcc12b0 100644 --- a/node/server.js +++ b/node/server.js @@ -9698,7 +9698,7 @@ function init_io() { instances[player.in].players[player.id] = player; pmap_add(player); - name_to_id.get(player.name) = socket.id; + name_to_id.set(player.name, socket.id); id_to_id[player.id] = socket.id; cache_player_items(player); From 369979ae0529bda59dd98853235f7411fe6e1373 Mon Sep 17 00:00:00 2001 From: FreezePhoenix Date: Sun, 5 Nov 2023 14:38:26 -0500 Subject: [PATCH 8/8] Fix one small bug lul --- node/server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/server.js b/node/server.js index 6dcc12b0..a9d8b1b2 100644 --- a/node/server.js +++ b/node/server.js @@ -1313,12 +1313,12 @@ function calculate_player_stats(player) { player.max_hp = max(1, player.max_hp); player.max_mp += player.int * 15.0 + player.level * 5; //player.armor+=player.str/2.0; - if(player.str > STR_ARMOR_DROPOFF) { + if (player.str > STR_ARMOR_DROPOFF) { player.armor += STR_ARMOR_DROPOFF + (player.str - STR_ARMOR_DROPOFF) / 4; } else { player.armor += player.str; } - if(player.int > INT_RESIST_DROPOFF) { + if (player.int > INT_RESIST_DROPOFF) { player.resistance += INT_RESIST_DROPOFF + (player.int - INT_RESIST_DROPOFF) / 4; } else { player.resistance += player.int;