chore: Configured autoformatter for engine code

This commit is contained in:
Pazaz 2024-02-01 12:15:33 -05:00
parent 4cf82db1b5
commit 8db6c53cf4
155 changed files with 2246 additions and 2029 deletions

View file

@ -1 +1,5 @@
data/*
prisma/*
public/*
ref/*
view/*

View file

@ -1,55 +1,38 @@
module.exports = {
'env': {
'browser': true,
'es2021': true,
'node': true
env: {
browser: true,
es2021: true,
node: true
},
'extends': [
'eslint:recommended',
'plugin:@typescript-eslint/recommended'
],
'overrides': [
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
overrides: [
{
'env': {
'node': true
env: {
node: true
},
'files': [
'.eslintrc.{js,cjs}'
],
'parserOptions': {
'sourceType': 'script'
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script'
}
}
],
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaVersion': 'latest',
'sourceType': 'module'
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
'plugins': [
'@typescript-eslint'
],
'rules': {
'indent': [
'error',
4,
{ 'SwitchCase': 1 }
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
],
plugins: ['@typescript-eslint'],
rules: {
indent: ['error', 4, { SwitchCase: 1 }],
quotes: ['error', 'single', { avoidEscape: true }],
semi: ['error', 'always'],
/**
* https://eslint.org/docs/latest/rules/no-constant-condition#checkloops
*
* Allows constant conditions in loops but not in if statements
*/
'no-constant-condition': ['error', { 'checkLoops': false }],
'no-constant-condition': ['error', { checkLoops: false }],
/**
* (jkm) this rule is included in the default ruleset, we should consider
@ -73,6 +56,6 @@ module.exports = {
*/
'@typescript-eslint/no-namespace': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-unused-vars': 'warn'
}
};
};

9
.prettierignore Normal file
View file

@ -0,0 +1,9 @@
data/*
prisma/*
public/*
ref/*
view/*
*.json
*.yml
*.md

11
.prettierrc Normal file
View file

@ -0,0 +1,11 @@
{
"printWidth": 250,
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid",
"endOfLine": "auto"
}

View file

@ -5,17 +5,17 @@ const config: Config = {
moduleNameMapper: {
'^#lostcity/(.*).js$': '<rootDir>/src/lostcity/$1',
'^#jagex2/(.*).js$': '<rootDir>/src/jagex2/$1',
'^(\\.{1,2}/.*)\\.js$': '$1',
'^(\\.{1,2}/.*)\\.js$': '$1'
},
transform: {
'^.+\\.[tj]sx?$': [
'ts-jest',
{
useESM: true,
},
],
},
useESM: true
}
]
}
};
export default config;

148
package-lock.json generated
View file

@ -43,6 +43,7 @@
"eslint": "^8.56.0",
"jest": "^29.7.0",
"nodemon": "^3.0.2",
"prettier": "3.2.4",
"prisma": "^5.7.1",
"prisma-kysely": "^1.7.1",
"ts-jest": "^29.1.1",
@ -2997,16 +2998,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.3.tgz",
"integrity": "sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz",
"integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==",
"dev": true,
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.7.3",
"@typescript-eslint/types": "6.7.3",
"@typescript-eslint/typescript-estree": "6.7.3",
"@typescript-eslint/visitor-keys": "6.7.3",
"@typescript-eslint/scope-manager": "6.20.0",
"@typescript-eslint/types": "6.20.0",
"@typescript-eslint/typescript-estree": "6.20.0",
"@typescript-eslint/visitor-keys": "6.20.0",
"debug": "^4.3.4"
},
"engines": {
@ -3026,14 +3027,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz",
"integrity": "sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz",
"integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==",
"dev": true,
"peer": true,
"dependencies": {
"@typescript-eslint/types": "6.7.3",
"@typescript-eslint/visitor-keys": "6.7.3"
"@typescript-eslint/types": "6.20.0",
"@typescript-eslint/visitor-keys": "6.20.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@ -3128,9 +3129,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.3.tgz",
"integrity": "sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz",
"integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==",
"dev": true,
"peer": true,
"engines": {
@ -3142,17 +3143,18 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz",
"integrity": "sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz",
"integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==",
"dev": true,
"peer": true,
"dependencies": {
"@typescript-eslint/types": "6.7.3",
"@typescript-eslint/visitor-keys": "6.7.3",
"@typescript-eslint/types": "6.20.0",
"@typescript-eslint/visitor-keys": "6.20.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "9.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
@ -3169,6 +3171,22 @@
}
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"peer": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz",
@ -3269,13 +3287,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz",
"integrity": "sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz",
"integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==",
"dev": true,
"peer": true,
"dependencies": {
"@typescript-eslint/types": "6.7.3",
"@typescript-eslint/types": "6.20.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
@ -7978,6 +7996,21 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz",
"integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@ -12220,28 +12253,28 @@
}
},
"@typescript-eslint/parser": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.3.tgz",
"integrity": "sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz",
"integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==",
"dev": true,
"peer": true,
"requires": {
"@typescript-eslint/scope-manager": "6.7.3",
"@typescript-eslint/types": "6.7.3",
"@typescript-eslint/typescript-estree": "6.7.3",
"@typescript-eslint/visitor-keys": "6.7.3",
"@typescript-eslint/scope-manager": "6.20.0",
"@typescript-eslint/types": "6.20.0",
"@typescript-eslint/typescript-estree": "6.20.0",
"@typescript-eslint/visitor-keys": "6.20.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz",
"integrity": "sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz",
"integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==",
"dev": true,
"peer": true,
"requires": {
"@typescript-eslint/types": "6.7.3",
"@typescript-eslint/visitor-keys": "6.7.3"
"@typescript-eslint/types": "6.20.0",
"@typescript-eslint/visitor-keys": "6.20.0"
}
},
"@typescript-eslint/type-utils": {
@ -12290,26 +12323,39 @@
}
},
"@typescript-eslint/types": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.3.tgz",
"integrity": "sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz",
"integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==",
"dev": true,
"peer": true
},
"@typescript-eslint/typescript-estree": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz",
"integrity": "sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz",
"integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==",
"dev": true,
"peer": true,
"requires": {
"@typescript-eslint/types": "6.7.3",
"@typescript-eslint/visitor-keys": "6.7.3",
"@typescript-eslint/types": "6.20.0",
"@typescript-eslint/visitor-keys": "6.20.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "9.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
"dependencies": {
"minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"peer": true,
"requires": {
"brace-expansion": "^2.0.1"
}
}
}
},
"@typescript-eslint/utils": {
@ -12371,13 +12417,13 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz",
"integrity": "sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz",
"integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==",
"dev": true,
"peer": true,
"requires": {
"@typescript-eslint/types": "6.7.3",
"@typescript-eslint/types": "6.20.0",
"eslint-visitor-keys": "^3.4.1"
}
},
@ -15935,6 +15981,12 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
"prettier": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz",
"integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==",
"dev": true
},
"pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",

View file

@ -68,6 +68,7 @@
"eslint": "^8.56.0",
"jest": "^29.7.0",
"nodemon": "^3.0.2",
"prettier": "3.2.4",
"prisma": "^5.7.1",
"prisma-kysely": "^1.7.1",
"ts-jest": "^29.1.1",

View file

@ -7,50 +7,25 @@ fs.mkdirSync('dump', { recursive: true });
// https://www.ncbi.nlm.nih.gov/IEB/ToolBox/CPP_DOC/lxr/source/src/util/compress/bzip2/crctable.c
const crc32Table = [
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
];
// generated from 1 << i, except for 32
const masks = [
0x00000000, 0x00000001, 0x00000003, 0x00000007,
0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, -0x80000000,
0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, -0x80000000
];
function createOrderedHuffmanTable(lengths) {
@ -76,7 +51,7 @@ function createOrderedHuffmanTable(lengths) {
break;
}
}
table.sort((a, b) => ((a.bits - b.bits) || (a.code - b.code)));
table.sort((a, b) => a.bits - b.bits || a.code - b.code);
let tempBits = 0;
let symbol = -1;
const fastAccess = [];
@ -94,7 +69,7 @@ function createOrderedHuffmanTable(lengths) {
}
return {
table,
fastAccess,
fastAccess
};
}
@ -113,10 +88,10 @@ function bwtReverse(src, primary) {
links.push(start[unsorted[i]]++); // eslint-disable-line no-plusplus
}
let i;
const first = src[i = primary];
const first = src[(i = primary)];
const ret = [];
for (let j = 1; j < src.length; j += 1) {
const x = src[i = links[i]];
const x = src[(i = links[i])];
if (x === undefined) {
ret.push(255);
} else {
@ -133,7 +108,7 @@ export default class BZip2 {
let index = 0;
let bitfield = 0;
let bits = 0;
const read = (n) => {
const read = n => {
if (n >= 32) {
const nd = n >> 1;
return read(nd) * (1 << nd) + read(n - nd);
@ -207,7 +182,8 @@ export default class BZip2 {
}
}
const v = mtf[c];
for (let j = c; j > 0; mtf[j] = mtf[--j]) { // eslint-disable-line no-plusplus
for (let j = c; j > 0; mtf[j] = mtf[--j]) {
// eslint-disable-line no-plusplus
// nothing
}
selectors.push(v);
@ -223,7 +199,7 @@ export default class BZip2 {
throw new Error('Huffman group length outside range');
}
while (read(1)) {
length -= (read(1) * 2) - 1;
length -= read(1) * 2 - 1;
}
lengths.push(length);
}
@ -262,7 +238,7 @@ export default class BZip2 {
}
r = t.fastAccess[b][bitfield >> (bits - b)];
if (r) {
bitfield &= masks[bits -= b];
bitfield &= masks[(bits -= b)];
r = r.code;
break;
}
@ -297,10 +273,7 @@ export default class BZip2 {
while (i < nt.length) {
const c = nt[i];
let count = 1;
if ((i < nt.length - 4)
&& nt[i + 1] === c
&& nt[i + 2] === c
&& nt[i + 3] === c) {
if (i < nt.length - 4 && nt[i + 1] === c && nt[i + 2] === c && nt[i + 3] === c) {
count = nt[i + 4] + 4;
i += 5;
} else {

View file

@ -14,83 +14,84 @@ export default class Isaac {
this.init();
}
// prettier-ignore
init(): void {
let a: number = 0x9e3779b9, b: number = 0x9e3779b9, c: number = 0x9e3779b9, d: number = 0x9e3779b9,
e: number = 0x9e3779b9, f: number = 0x9e3779b9, g: number = 0x9e3779b9, h: number = 0x9e3779b9;
for (let i: number = 0; i < 4; i++) {
a ^= b << 11; d += a; b += c;
b ^= c >>> 2; e += b; c += d;
c ^= d << 8; f += c; d += e;
d ^= e >>> 16; g += d; e += f;
e ^= f << 10; h += e; f += g;
f ^= g >>> 4; a += f; g += h;
g ^= h << 8; b += g; h += a;
h ^= a >>> 9; c += h; a += b;
}
for (let i: number = 0; i < 256; i += 8) {
a += this.rsl[i];
b += this.rsl[i + 1];
c += this.rsl[i + 2];
d += this.rsl[i + 3];
e += this.rsl[i + 4];
f += this.rsl[i + 5];
g += this.rsl[i + 6];
h += this.rsl[i + 7];
a ^= b << 11; d += a; b += c;
b ^= c >>> 2; e += b; c += d;
c ^= d << 8; f += c; d += e;
d ^= e >>> 16; g += d; e += f;
e ^= f << 10; h += e; f += g;
f ^= g >>> 4; a += f; g += h;
g ^= h << 8; b += g; h += a;
h ^= a >>> 9; c += h; a += b;
this.mem[i] = a;
this.mem[i + 1] = b;
this.mem[i + 2] = c;
this.mem[i + 3] = d;
this.mem[i + 4] = e;
this.mem[i + 5] = f;
this.mem[i + 6] = g;
this.mem[i + 7] = h;
}
for (let i: number = 0; i < 256; i += 8) {
a += this.mem[i];
b += this.mem[i + 1];
c += this.mem[i + 2];
d += this.mem[i + 3];
e += this.mem[i + 4];
f += this.mem[i + 5];
g += this.mem[i + 6];
h += this.mem[i + 7];
a ^= b << 11; d += a; b += c;
b ^= c >>> 2; e += b; c += d;
c ^= d << 8; f += c; d += e;
d ^= e >>> 16; g += d; e += f;
e ^= f << 10; h += e; f += g;
f ^= g >>> 4; a += f; g += h;
g ^= h << 8; b += g; h += a;
h ^= a >>> 9; c += h; a += b;
this.mem[i] = a;
this.mem[i + 1] = b;
this.mem[i + 2] = c;
this.mem[i + 3] = d;
this.mem[i + 4] = e;
this.mem[i + 5] = f;
this.mem[i + 6] = g;
this.mem[i + 7] = h;
}
this.isaac();
this.count = 256;
let a: number = 0x9e3779b9, b: number = 0x9e3779b9, c: number = 0x9e3779b9, d: number = 0x9e3779b9,
e: number = 0x9e3779b9, f: number = 0x9e3779b9, g: number = 0x9e3779b9, h: number = 0x9e3779b9;
for (let i: number = 0; i < 4; i++) {
a ^= b << 11; d += a; b += c;
b ^= c >>> 2; e += b; c += d;
c ^= d << 8; f += c; d += e;
d ^= e >>> 16; g += d; e += f;
e ^= f << 10; h += e; f += g;
f ^= g >>> 4; a += f; g += h;
g ^= h << 8; b += g; h += a;
h ^= a >>> 9; c += h; a += b;
}
for (let i: number = 0; i < 256; i += 8) {
a += this.rsl[i];
b += this.rsl[i + 1];
c += this.rsl[i + 2];
d += this.rsl[i + 3];
e += this.rsl[i + 4];
f += this.rsl[i + 5];
g += this.rsl[i + 6];
h += this.rsl[i + 7];
a ^= b << 11; d += a; b += c;
b ^= c >>> 2; e += b; c += d;
c ^= d << 8; f += c; d += e;
d ^= e >>> 16; g += d; e += f;
e ^= f << 10; h += e; f += g;
f ^= g >>> 4; a += f; g += h;
g ^= h << 8; b += g; h += a;
h ^= a >>> 9; c += h; a += b;
this.mem[i] = a;
this.mem[i + 1] = b;
this.mem[i + 2] = c;
this.mem[i + 3] = d;
this.mem[i + 4] = e;
this.mem[i + 5] = f;
this.mem[i + 6] = g;
this.mem[i + 7] = h;
}
for (let i: number = 0; i < 256; i += 8) {
a += this.mem[i];
b += this.mem[i + 1];
c += this.mem[i + 2];
d += this.mem[i + 3];
e += this.mem[i + 4];
f += this.mem[i + 5];
g += this.mem[i + 6];
h += this.mem[i + 7];
a ^= b << 11; d += a; b += c;
b ^= c >>> 2; e += b; c += d;
c ^= d << 8; f += c; d += e;
d ^= e >>> 16; g += d; e += f;
e ^= f << 10; h += e; f += g;
f ^= g >>> 4; a += f; g += h;
g ^= h << 8; b += g; h += a;
h ^= a >>> 9; c += h; a += b;
this.mem[i] = a;
this.mem[i + 1] = b;
this.mem[i + 2] = c;
this.mem[i + 3] = d;
this.mem[i + 4] = e;
this.mem[i + 5] = f;
this.mem[i + 6] = g;
this.mem[i + 7] = h;
}
this.isaac();
this.count = 256;
}
isaac(): void {
this.c++;
this.b += this.c;
@ -113,11 +114,11 @@ export default class Isaac {
break;
}
this.a += this.mem[(i + 128) & 0xFF];
this.a += this.mem[(i + 128) & 0xff];
let y: number;
this.mem[i] = y = this.mem[(x >>> 2) & 0xFF] + this.a + this.b;
this.rsl[i] = this.b = this.mem[((y >>> 8) >>> 2) & 0xFF] + x;
this.mem[i] = y = this.mem[(x >>> 2) & 0xff] + this.a + this.b;
this.rsl[i] = this.b = this.mem[((y >>> 8) >>> 2) & 0xff] + x;
}
}

View file

@ -7,20 +7,20 @@ function genHash(name: string): number {
let hash: number = 0;
name = name.toUpperCase();
for (let i: number = 0; i < name.length; i++) {
hash = ((hash * 61 + name.charCodeAt(i)) - 32) | 0;
hash = (hash * 61 + name.charCodeAt(i) - 32) | 0;
}
return hash;
}
type JagQueueFile = {
hash: number,
name: string,
data?: Packet,
write?: boolean,
delete?: boolean,
rename?: boolean,
newName?: string,
newHash?: number,
hash: number;
name: string;
data?: Packet;
write?: boolean;
delete?: boolean;
rename?: boolean;
newName?: string;
newHash?: number;
};
export default class Jagfile {
@ -120,7 +120,13 @@ export default class Jagfile {
const oldHash: number = genHash(oldName);
const newHash: number = genHash(newName);
this.fileQueue.push({ hash: oldHash, name: oldName, rename: true, newName, newHash });
this.fileQueue.push({
hash: oldHash,
name: oldName,
rename: true,
newName,
newHash
});
}
save(path: string, doNotCompressWhole: boolean = false): Packet {
@ -479,7 +485,7 @@ for (let i: number = 0; i < KNOWN_NAMES.length; i++) {
str = str.toUpperCase();
for (let i: number = 0; i < str.length; i++) {
hash = ((hash * 61 + str.charCodeAt(i)) - 32) | 0;
hash = (hash * 61 + str.charCodeAt(i) - 32) | 0;
}
KNOWN_HASHES[i] = hash;

View file

@ -257,4 +257,4 @@ describe('Packet', () => {
expect(result.gPacket()).toStrictEqual(expected);
});
});
});
});

View file

@ -4,7 +4,7 @@ import { dirname } from 'path';
export default class Packet {
static crctable: Int32Array = new Int32Array(256);
static CRC32_POLYNOMIAL: number = 0xEDB88320;
static CRC32_POLYNOMIAL: number = 0xedb88320;
static bitmask: Uint32Array = new Uint32Array(33);
@ -12,7 +12,7 @@ export default class Packet {
for (let i: number = 0; i < 32; i++) {
Packet.bitmask[i] = (1 << i) - 1;
}
Packet.bitmask[32] = 0xFFFFFFFF;
Packet.bitmask[32] = 0xffffffff;
for (let i: number = 0; i < 256; i++) {
let remainder: number = i;
@ -79,10 +79,10 @@ export default class Packet {
src = src.data;
}
let crc: number = 0xFFFFFFFF;
let crc: number = 0xffffffff;
for (let i: number = offset; i < offset + length; i++) {
crc = crc >>> 8 ^ Packet.crctable[(crc ^ src[i]) & 0xFF];
crc = (crc >>> 8) ^ Packet.crctable[(crc ^ src[i]) & 0xff];
}
return ~crc;
@ -155,7 +155,7 @@ export default class Packet {
p8(value: bigint): void {
this.ensure(8);
this.p4(Number(value >> 32n));
this.p4(Number(value & 0xFFFFFFFFn));
this.p4(Number(value & 0xffffffffn));
}
pjstr(str: string | null): void {
@ -224,7 +224,7 @@ export default class Packet {
if (value < 0x40 && value >= -0x40) {
this.p1(value + 0x40);
} else if (value < 0x4000 && value >= -0x4000) {
this.p2(value + 0xC000);
this.p2(value + 0xc000);
} else {
console.trace(`Error psmarts out of range: ${value}`);
}
@ -242,7 +242,7 @@ export default class Packet {
g1s(): number {
let value: number = this.g1();
if (value > 0x7F) {
if (value > 0x7f) {
value -= 0x100;
}
return value;
@ -253,12 +253,12 @@ export default class Packet {
}
ig2(): number {
return (this.data[this.pos++] >>> 0 | (this.data[this.pos++]) << 8);
return (this.data[this.pos++] >>> 0) | (this.data[this.pos++] << 8);
}
g2s(): number {
let value: number = this.g2();
if (value > 0x7FFF) {
if (value > 0x7fff) {
value -= 0x10000;
}
return value;
@ -273,12 +273,12 @@ export default class Packet {
}
ig4(): number {
return (this.data[this.pos++] >>> 0 | (this.data[this.pos++] << 8) | (this.data[this.pos++] << 16) | (this.data[this.pos++] << 24));
return (this.data[this.pos++] >>> 0) | (this.data[this.pos++] << 8) | (this.data[this.pos++] << 16) | (this.data[this.pos++] << 24);
}
g4s(): number {
let value: number = this.g4();
if (value > 0x7FFFFFFF) {
if (value > 0x7fffffff) {
value -= 0x100000000;
}
return value;
@ -319,11 +319,11 @@ export default class Packet {
}
gsmart(): number {
return (this.data[this.pos] & 0xFF) < 0x80 ? this.g1() : this.g2() - 0x8000;
return (this.data[this.pos] & 0xff) < 0x80 ? this.g1() : this.g2() - 0x8000;
}
gsmarts(): number {
return (this.data[this.pos] & 0xFF) < 0x80 ? this.g1() - 0x40 : this.g2() - 0xC000;
return (this.data[this.pos] & 0xff) < 0x80 ? this.g1() - 0x40 : this.g2() - 0xc000;
}
// ----

View file

@ -6,23 +6,27 @@ export function toBase37(string: string): bigint {
const c: number = string.charCodeAt(i);
l *= 37n;
if (c >= 0x41 && c <= 0x5A) { // A-Z
l += BigInt((c + 1) - 0x41);
} else if (c >= 0x61 && c <= 0x7A) { // a-z
l += BigInt((c + 1) - 0x61);
} else if (c >= 0x30 && c <= 0x39) { // 0-9
l += BigInt((c + 27) - 0x30);
if (c >= 0x41 && c <= 0x5a) {
// A-Z
l += BigInt(c + 1 - 0x41);
} else if (c >= 0x61 && c <= 0x7a) {
// a-z
l += BigInt(c + 1 - 0x61);
} else if (c >= 0x30 && c <= 0x39) {
// 0-9
l += BigInt(c + 27 - 0x30);
}
}
return l;
}
// prettier-ignore
const BASE37_LOOKUP: string[] = [
'_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
'_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
];
export function fromBase37(value: bigint): string {

View file

@ -1,14 +1,15 @@
import Packet from '#jagex2/io/Packet.js';
export default class TextEncoder {
// prettier-ignore
private static CHAR_LOOKUP: string[] = [
' ',
'e', 't', 'a', 'o', 'i', 'h', 'n', 's', 'r', 'd', 'l', 'u', 'm',
'w', 'c', 'y', 'f', 'g', 'p', 'b', 'v', 'k', 'x', 'j', 'q', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
' ', '!', '?', '.', ',', ':', ';', '(', ')', '-',
'&', '*', '\\', '\'', '@', '#', '+', '=', '£', '$', '%', '"', '[', ']'
];
' ',
'e', 't', 'a', 'o', 'i', 'h', 'n', 's', 'r', 'd', 'l', 'u', 'm',
'w', 'c', 'y', 'f', 'g', 'p', 'b', 'v', 'k', 'x', 'j', 'q', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
' ', '!', '?', '.', ',', ':', ';', '(', ')', '-',
'&', '*', '\\', '\'', '@', '#', '+', '=', '£', '$', '%', '"', '[', ']'
];
static decode(packet: Packet, length: number): string {
const charBuffer: string[] = [];
@ -17,7 +18,7 @@ export default class TextEncoder {
let nibble: number;
for (let i: number = 0; i < length && pos < 100; i++) {
const data: number = packet.g1();
nibble = data >> 4 & 0xF;
nibble = (data >> 4) & 0xf;
if (carry !== -1) {
charBuffer[pos++] = this.CHAR_LOOKUP[(carry << 4) + nibble - 195];
carry = -1;
@ -26,7 +27,7 @@ export default class TextEncoder {
} else {
carry = nibble;
}
nibble = data & 0xF;
nibble = data & 0xf;
if (carry != -1) {
charBuffer[pos++] = this.CHAR_LOOKUP[(carry << 4) + nibble - 195];
carry = -1;
@ -68,7 +69,7 @@ export default class TextEncoder {
carry = -1;
} else {
packet.p1((carry << 4) + (index >> 4));
carry = index & 0xF;
carry = index & 0xf;
}
}
if (carry != -1) {

View file

@ -12,7 +12,7 @@ import Environment from '#lostcity/util/Environment.js';
if (!fs.existsSync('.env')) {
console.error('Missing .env file');
console.error('Please make sure you have a .env file in the main directory, copy and rename .env.example if you don\'t have one');
console.error("Please make sure you have a .env file in the main directory, copy and rename .env.example if you don't have one");
process.exit(1);
}
@ -34,7 +34,7 @@ const wsServer = new WSServer();
wsServer.start();
let exiting = false;
process.on('SIGINT', function() {
process.on('SIGINT', function () {
if (exiting) {
return;
}

View file

@ -103,7 +103,7 @@ export default class DbRowType extends ConfigType {
for (let fieldId = 0; fieldId < fieldCount; fieldId++) {
for (let typeId = 0; typeId < types.length; typeId++) {
const type = types[typeId];
const index = typeId + (fieldId * types.length);
const index = typeId + fieldId * types.length;
if (type === ScriptVarType.STRING) {
values[index] = packet.gjstr();

View file

@ -61,7 +61,7 @@ export default class DbTableType extends ConfigType {
this.types = new Array(packet.g1());
for (let setting = packet.g1(); setting != 255; setting = packet.g1()) {
const column = setting & 0x7F;
const column = setting & 0x7f;
const hasDefault = (setting & 0x80) !== 0;
const columnTypes: number[] = new Array(packet.g1());
@ -93,7 +93,7 @@ export default class DbTableType extends ConfigType {
getDefault(column: number) {
if (!this.defaultValues[column]) {
const defaults: (ReturnType<typeof ScriptVarType.getDefault>)[] = [];
const defaults: ReturnType<typeof ScriptVarType.getDefault>[] = [];
for (let i = 0; i < this.types[column].length; i++) {
defaults[i] = ScriptVarType.getDefault(this.types[column][i]);
}
@ -111,7 +111,7 @@ export default class DbTableType extends ConfigType {
for (let fieldId = 0; fieldId < fieldCount; fieldId++) {
for (let typeId = 0; typeId < types.length; typeId++) {
const type = types[typeId];
const index = typeId + (fieldId * types.length);
const index = typeId + fieldId * types.length;
if (type === ScriptVarType.STRING) {
values[index] = packet.gjstr();

View file

@ -58,8 +58,8 @@ export default class FontType {
this.charOffsetX[i] = idx.g1();
this.charOffsetY[i] = idx.g1();
const w = this.charMaskWidth[i] = idx.g2();
const h = this.charMaskHeight[i] = idx.g2();
const w = (this.charMaskWidth[i] = idx.g2());
const h = (this.charMaskHeight[i] = idx.g2());
const type = idx.g1();
const len = w * h;
@ -73,7 +73,7 @@ export default class FontType {
} else if (type == 1) {
for (let x = 0; x < w; x++) {
for (let y = 0; y < h; y++) {
this.charMask[i][x + (y * w)] = dat.g1();
this.charMask[i][x + y * w] = dat.g1();
}
}
}
@ -101,7 +101,7 @@ export default class FontType {
space = 0;
for (let y = Math.floor(h / 7); y < h; y++) {
space += this.charMask[i][w + (y * w) - 1];
space += this.charMask[i][w + y * w - 1];
}
if (space <= Math.floor(h / 7)) {

View file

@ -132,4 +132,4 @@ describe('IdkType', () => {
expect(idk.debugname).toBe('jordan');
});
});
});
});

View file

@ -922,4 +922,4 @@ describe('IfType', () => {
expect(ifType.option).toBeNull();
});
});
});
});

View file

@ -60,7 +60,7 @@ export default class IfType {
if (com.overLayer == 0) {
com.overLayer = -1;
} else {
com.overLayer = (com.overLayer - 1 << 8) + dat.g1();
com.overLayer = ((com.overLayer - 1) << 8) + dat.g1();
}
const comparatorCount = dat.g1();
@ -174,14 +174,14 @@ export default class IfType {
if (com.anim == 0) {
com.anim = -1;
} else {
com.anim = (com.anim - 1 << 8) + dat.g1();
com.anim = ((com.anim - 1) << 8) + dat.g1();
}
com.activeAnim = dat.g1();
if (com.activeAnim == 0) {
com.activeAnim = -1;
} else {
com.activeAnim = (com.activeAnim - 1 << 8) + dat.g1();
com.activeAnim = ((com.activeAnim - 1) << 8) + dat.g1();
}
com.zoom = dat.g2();

View file

@ -27,7 +27,7 @@ export default class LocType extends ConfigType {
config.decodeType(dat);
if (config.active === -1) {
config.active = (config.shapes.length > 0 && config.shapes[0] === 10) ? 1 : 0;
config.active = config.shapes.length > 0 && config.shapes[0] === 10 ? 1 : 0;
if (config.ops.length > 0) {
config.active = 1;

View file

@ -8,23 +8,23 @@ describe('MesanimType', () => {
describe('static load', () => {
it('should load data from mesanim.dat', () => {
const dat = new Packet();
fs.existsSync = jest.fn().mockReturnValue(true);
Packet.load = jest.fn().mockReturnValue(dat);
MesanimType.load('/path/to/data');
expect(Packet.load).toHaveBeenCalledWith('/path/to/data/mesanim.dat');
});
it('should return early if mesanim.dat does not exist', () => {
fs.existsSync = jest.fn().mockReturnValue(false);
Packet.load = jest.fn().mockReturnValue(undefined);
expect(MesanimType.load('/path/to/data')).toBeUndefined();
expect(Packet.load).not.toHaveBeenCalled();
});
it('should decode packet', () => {
const packet = new Packet();
packet.p1(1); // opcode
@ -34,13 +34,13 @@ describe('MesanimType', () => {
packet.p1(250); // opcode
packet.pjstr('testName');
packet.pos = 0; // set pos to 0 for reading
const instance = new MesanimType(1);
instance.decodeType(packet);
expect(instance.len[0]).toBe(2);
expect(instance.len[3]).toBe(5);
expect(instance.debugname).toBe('testName');
});
});
});
});

View file

@ -152,7 +152,7 @@ export default class ObjType extends ConfigType {
tradeable = false;
respawnrate = 100; // default to 1-minute
params: ParamMap = new Map();
decode(code: number, dat: Packet): void {
if (code === 1) {
this.model = dat.g2();

View file

@ -1,6 +1,6 @@
import Packet from '#jagex2/io/Packet.js';
export type ParamMap = Map<number, number | string>
export type ParamMap = Map<number, number | string>;
export interface ParamHolder {
params: ParamMap | null;

View file

@ -1,6 +1,6 @@
import fs from 'fs';
import Packet from '#jagex2/io/Packet.js';
import {ConfigType} from '#lostcity/cache/ConfigType.js';
import { ConfigType } from '#lostcity/cache/ConfigType.js';
import ScriptVarType from '#lostcity/cache/ScriptVarType.js';
export default class VarSharedType extends ConfigType {

View file

@ -74,8 +74,8 @@ describe('WordEnc', () => {
expect(WordEnc.filter(TextEncoder.toSentenceCase('c0m, badword'))).toBe('***, badword');
expect(WordEnc.filter(TextEncoder.toSentenceCase('----vv vv vv rswalmart c - 0 - nn sell cheap gold 1000k "="2.1(.u.s\'d)'))).toBe('----******** rswalmart c - 0 - nn sell cheap gold 1000k "="2.1(.*****)');
expect(WordEnc.filter(TextEncoder.toSentenceCase('Web:---4 r s_gold_c"..0..""\'|\\/|""cheap rs gold -20 \'m\'=18.3\'$'))).toBe('Web:---4 r s_gold_********************* rs gold -20 \'m\'=18.3\'$');
expect(WordEnc.filter(TextEncoder.toSentenceCase('Cheap sell gold>google open:___\'fzf\'__c"..0..\'|\\/|"">20m=17.23$'))).toBe('Cheap sell gold>google open:___\'fzf\'__******************=17.23$');
expect(WordEnc.filter(TextEncoder.toSentenceCase('Web:---4 r s_gold_c"..0..""\'|\\/|""cheap rs gold -20 \'m\'=18.3\'$'))).toBe("Web:---4 r s_gold_********************* rs gold -20 'm'=18.3'$");
expect(WordEnc.filter(TextEncoder.toSentenceCase('Cheap sell gold>google open:___\'fzf\'__c"..0..\'|\\/|"">20m=17.23$'))).toBe("Cheap sell gold>google open:___'fzf'__******************=17.23$");
expect(WordEnc.filter(TextEncoder.toSentenceCase('..:::.4 r s g 0 l d..:::c:::0:::/y\\>>>20""m = 18.3----usd.'))).toBe('..:::.4 R s g 0 l ****************\\>>>***** = 18.3----Usd.');
});

View file

@ -9,16 +9,31 @@ import WordEncDomains from '#lostcity/cache/WordEncDomains.js';
import WordEncTlds from '#lostcity/cache/WordEncTlds.js';
export default class WordEnc {
static PERIOD = new Uint16Array(['d', 'o', 't'].join('').split('').map((char) => char.charCodeAt(0)));
static AMPERSAT = new Uint16Array(['(', 'a', ')'].join('').split('').map((char) => char.charCodeAt(0)));
static SLASH = new Uint16Array(['s', 'l', 'a', 's', 'h'].join('').split('').map((char) => char.charCodeAt(0)));
static PERIOD = new Uint16Array(
['d', 'o', 't']
.join('')
.split('')
.map(char => char.charCodeAt(0))
);
static AMPERSAT = new Uint16Array(
['(', 'a', ')']
.join('')
.split('')
.map(char => char.charCodeAt(0))
);
static SLASH = new Uint16Array(
['s', 'l', 'a', 's', 'h']
.join('')
.split('')
.map(char => char.charCodeAt(0))
);
private static wordEncFragments = new WordEncFragments();
private static wordEncBadWords = new WordEncBadWords(this.wordEncFragments);
private static wordEncDomains = new WordEncDomains(this.wordEncBadWords);
private static wordEncTlds = new WordEncTlds(this.wordEncBadWords, this.wordEncDomains);
private static whitelist = ['cook', 'cook\'s', 'cooks', 'seeks', 'sheet'];
private static whitelist = ['cook', "cook's", 'cooks', 'seeks', 'sheet'];
static load(dir: string): void {
if (!fs.existsSync(`${dir}/wordenc`)) {
@ -226,7 +241,7 @@ export default class WordEnc {
}
private static isCharacterAllowed(char: string): boolean {
return char >= ' ' && char <= '\u007f' || char == ' ' || char == '\n' || char == '\t' || char == '£' || char == '€';
return (char >= ' ' && char <= '\u007f') || char == ' ' || char == '\n' || char == '\t' || char == '£' || char == '€';
}
private static replaceUppercases(chars: string[], comparison: string[]): void {

View file

@ -37,10 +37,10 @@ export default class WordEncBadWords {
if (hasSymbol) {
let isBeforeSymbol = false;
let isAfterSymbol = false;
if (startIndex - 1 < 0 || WordEnc.isSymbol(chars[startIndex - 1]) && chars[startIndex - 1] != '\'') {
if (startIndex - 1 < 0 || (WordEnc.isSymbol(chars[startIndex - 1]) && chars[startIndex - 1] != "'")) {
isBeforeSymbol = true;
}
if (currentIndex >= chars.length || WordEnc.isSymbol(chars[currentIndex]) && chars[currentIndex] != '\'') {
if (currentIndex >= chars.length || (WordEnc.isSymbol(chars[currentIndex]) && chars[currentIndex] != "'")) {
isAfterSymbol = true;
}
if (!isBeforeSymbol || !isAfterSymbol) {
@ -50,17 +50,21 @@ export default class WordEncBadWords {
localIndex = startIndex;
}
while (!isSubstringValid && localIndex < currentIndex) {
if (localIndex >= 0 && (!WordEnc.isSymbol(chars[localIndex]) || chars[localIndex] == '\'')) {
if (localIndex >= 0 && (!WordEnc.isSymbol(chars[localIndex]) || chars[localIndex] == "'")) {
const localSubString: string[] = [];
let localSubStringIndex;
for (localSubStringIndex = 0; localSubStringIndex < 3 && localIndex + localSubStringIndex < chars.length && (!WordEnc.isSymbol(chars[localIndex + localSubStringIndex]) || chars[localIndex + localSubStringIndex] == '\''); localSubStringIndex++) {
for (
localSubStringIndex = 0;
localSubStringIndex < 3 && localIndex + localSubStringIndex < chars.length && (!WordEnc.isSymbol(chars[localIndex + localSubStringIndex]) || chars[localIndex + localSubStringIndex] == "'");
localSubStringIndex++
) {
localSubString[localSubStringIndex] = chars[localIndex + localSubStringIndex];
}
let isSubStringValidCondition = true;
if (localSubStringIndex == 0) {
isSubStringValidCondition = false;
}
if (localSubStringIndex < 3 && localIndex - 1 >= 0 && (!WordEnc.isSymbol(chars[localIndex - 1]) || chars[localIndex - 1] == '\'')) {
if (localSubStringIndex < 3 && localIndex - 1 >= 0 && (!WordEnc.isSymbol(chars[localIndex - 1]) || chars[localIndex - 1] == "'")) {
isSubStringValidCondition = false;
}
if (isSubStringValidCondition && !this.wordEncFragments.isBadFragment(localSubString)) {
@ -106,7 +110,17 @@ export default class WordEncBadWords {
}
}
private processBadCharacters(chars: string[], bads: Uint16Array, startIndex: number): { currentIndex: number; badIndex: number; hasSymbol: boolean; hasNumber: boolean; hasDigit: boolean; } {
private processBadCharacters(
chars: string[],
bads: Uint16Array,
startIndex: number
): {
currentIndex: number;
badIndex: number;
hasSymbol: boolean;
hasNumber: boolean;
hasDigit: boolean;
} {
let index = startIndex;
let badIndex = 0;
let count = 0;
@ -114,8 +128,8 @@ export default class WordEncBadWords {
let hasNumber = false;
let hasDigit = false;
for (; index < chars.length && !(hasNumber && hasDigit);) {
if (index >= chars.length || hasNumber && hasDigit) {
for (; index < chars.length && !(hasNumber && hasDigit); ) {
if (index >= chars.length || (hasNumber && hasDigit)) {
break;
}
const currentChar = chars[index];
@ -142,7 +156,7 @@ export default class WordEncBadWords {
if (badIndex >= bads.length || !WordEnc.isNotLowercaseAlpha(currentChar)) {
break;
}
if (WordEnc.isSymbol(currentChar) && currentChar !== '\'') {
if (WordEnc.isSymbol(currentChar) && currentChar !== "'") {
hasSymbol = true;
}
if (WordEnc.isNumerical(currentChar)) {
@ -150,13 +164,19 @@ export default class WordEncBadWords {
}
index++;
count++;
if (count * 100 / (index - startIndex) > 90) {
if ((count * 100) / (index - startIndex) > 90) {
break;
}
}
}
}
return { currentIndex: index, badIndex, hasSymbol, hasNumber, hasDigit };
return {
currentIndex: index,
badIndex,
hasSymbol,
hasNumber,
hasDigit
};
}
private getEmulatedBadCharLen(nextChar: string, badChar: string, currentChar: string): number {
@ -355,7 +375,7 @@ export default class WordEncBadWords {
private getIndex(char: string): number {
if (WordEnc.isLowercaseAlpha(char)) {
return char.charCodeAt(0) + 1 - 'a'.charCodeAt(0);
} else if (char == '\'') {
} else if (char == "'") {
return 28;
} else if (WordEnc.isNumerical(char)) {
return char.charCodeAt(0) + 29 - '0'.charCodeAt(0);

View file

@ -4,7 +4,7 @@ export default class WordEncFragments {
readonly fragments: number[] = [];
filter(chars: string[]): void {
for (let currentIndex = 0; currentIndex < chars.length;) {
for (let currentIndex = 0; currentIndex < chars.length; ) {
const numberIndex = this.indexOfNumber(chars, currentIndex);
if (numberIndex === -1) {
return;
@ -47,7 +47,6 @@ export default class WordEncFragments {
}
}
isBadFragment(chars: string[]): boolean {
if (WordEnc.isNumericalChars(chars)) {
return true;
@ -86,7 +85,7 @@ export default class WordEncFragments {
const char = chars[chars.length - index - 1];
if (WordEnc.isLowercaseAlpha(char)) {
value = value * 38 + char.charCodeAt(0) + 1 - 'a'.charCodeAt(0);
} else if (char == '\'') {
} else if (char == "'") {
value = value * 38 + 27;
} else if (WordEnc.isNumerical(char)) {
value = value * 38 + char.charCodeAt(0) + 28 - '0'.charCodeAt(0);

View file

@ -39,7 +39,7 @@ export default class WordEncTlds {
if (tldType == 1 && periodFilterStatus > 0 && slashFilterStatus > 0) {
shouldFilter = true;
}
if (tldType == 2 && (periodFilterStatus > 2 && slashFilterStatus > 0 || periodFilterStatus > 0 && slashFilterStatus > 2)) {
if (tldType == 2 && ((periodFilterStatus > 2 && slashFilterStatus > 0) || (periodFilterStatus > 0 && slashFilterStatus > 2))) {
shouldFilter = true;
}
if (tldType == 3 && periodFilterStatus > 0 && slashFilterStatus > 2) {
@ -112,11 +112,11 @@ export default class WordEncTlds {
}
}
private processTlds(chars: string[], tld: Uint16Array, currentIndex: number): { currentIndex: number, tldIndex: number } {
private processTlds(chars: string[], tld: Uint16Array, currentIndex: number): { currentIndex: number; tldIndex: number } {
let tldIndex = 0;
while (currentIndex < chars.length && tldIndex < tld.length) {
const currentChar = chars[currentIndex];
const nextChar = (currentIndex + 1 < chars.length) ? chars[currentIndex + 1] : '\u0000';
const nextChar = currentIndex + 1 < chars.length ? chars[currentIndex + 1] : '\u0000';
let currentLength: number;
if ((currentLength = this.wordEncDomains.getEmulatedDomainCharLen(nextChar, String.fromCharCode(tld[tldIndex]), currentChar)) > 0) {

View file

@ -5,12 +5,13 @@ import { Kysely, MysqlDialect } from 'kysely';
import Environment from '#lostcity/util/Environment.js';
const dialect = new MysqlDialect({
pool: async () => createPool({
database: Environment.DB_NAME as string,
host: Environment.DB_HOST as string,
user: Environment.DB_USER as string,
password: Environment.DB_PASS as string
})
pool: async () =>
createPool({
database: Environment.DB_NAME as string,
host: Environment.DB_HOST as string,
user: Environment.DB_USER as string,
password: Environment.DB_PASS as string
})
});
export const db = new Kysely<DB>({

View file

@ -1,6 +1,7 @@
import { db } from '#lostcity/db/query.js';
await db.insertInto('newspost_category')
await db
.insertInto('newspost_category')
.values([
{ id: 1, name: 'Game Updates', style: 'red' },
{ id: 2, name: 'Website', style: 'lblue' },
@ -8,11 +9,13 @@ await db.insertInto('newspost_category')
{ id: 4, name: 'Technical', style: 'dblue' },
{ id: 5, name: 'Community', style: 'green' },
{ id: 6, name: 'Behind the Scenes', style: 'purple' },
{ id: 7, name: 'Archived', style: 'white' },
{ id: 7, name: 'Archived', style: 'white' }
])
.ignore().execute();
.ignore()
.execute();
await db.insertInto('newspost')
await db
.insertInto('newspost')
.values([
// ---- rip history
// https://web.archive.org/web/20001110100100/http://www.jagex.com/runescape.html
@ -232,15 +235,15 @@ await db.insertInto('newspost')
// https://web.archive.org/web/20010301085507/http://www.runescape.com/
{
category_id: 7,
title: '\'Super strength Beer cheat\' fixed.',
content: 'Today it came to my attention that by drinking huges amount of beer players were able to make themselves super strong, and then go killing everyone! This has now been fixed so Beer can only increase your strength slightly. Many thanks to the person who told me about this one. If you know of any more cheats/tricks let me know so I can fix them and make RuneScape a fair place for everyone.',
title: "'Super strength Beer cheat' fixed.",
content:
'Today it came to my attention that by drinking huges amount of beer players were able to make themselves super strong, and then go killing everyone! This has now been fixed so Beer can only increase your strength slightly. Many thanks to the person who told me about this one. If you know of any more cheats/tricks let me know so I can fix them and make RuneScape a fair place for everyone.',
date: '2001-01-25'
},
{
category_id: 7,
title: 'Lots of improvements to the game',
content:
`Ranged combat - You now gain experience for each successful hit, so it's easier to advance a level. Arrows also now fire 25% faster, and I've fixed the cheat/bug which some people were using to shoot super rapidly.
content: `Ranged combat - You now gain experience for each successful hit, so it's easier to advance a level. Arrows also now fire 25% faster, and I've fixed the cheat/bug which some people were using to shoot super rapidly.
<p>
Magic - 4 new spells have been added which are: Fear, Wind-bolt, Rock-skin, and Elemental-bolt. The bolt spells have also been made much more powerful, and they are no longer dependant on your ranged-combat skill. This means each type of bolt now does a fixed maximum-damage, and to shoot more powerful bolts you need to use high level spells. Finally the staffs have been improved such that if you are wielding a fire-staff, water-staff, air-staff or earth-staff it acts as unlimited runes of that type!
<p>
@ -250,14 +253,14 @@ Player killing - Due to a large number of requests I've changed the way this wor
{
category_id: 7,
title: 'New Quest: Vampire Slayer',
content: 'The vampire sleeping in the coffin under Draynor Manor has been terrorising the inhabitants of the small village to the south and must be stopped! If you think you are up to the task of slaying a vampire, go and talk to the people in the village to find out how you can destroy this evil monster!',
content:
'The vampire sleeping in the coffin under Draynor Manor has been terrorising the inhabitants of the small village to the south and must be stopped! If you think you are up to the task of slaying a vampire, go and talk to the people in the village to find out how you can destroy this evil monster!',
date: '2001-01-27'
},
{
category_id: 7,
title: 'Further combat improvements',
content:
`Following saturdays update the combat system has been tweaked further. The retreating was still too difficult (particularly when being attacked by multiple players/monsters at once) so this has been improved and made easier.
content: `Following saturdays update the combat system has been tweaked further. The retreating was still too difficult (particularly when being attacked by multiple players/monsters at once) so this has been improved and made easier.
<p>
The "keep 3 best items" rule has also been modified. If you attack another player then you now lose the item-protection for 20 minutes of game time! This makes the player killing aspect of the game more dangerous and exciting, but it also increases your potential reward, as if you kill someone else without item-protection you can now get the best loot! People without item-protection have a small skull drawn above them.
<p>
@ -267,20 +270,20 @@ After many requests the guards in Varrock city now attempt to stop people fighti
{
category_id: 7,
title: 'New section of sewer opened',
content: 'A new area at the end of the sewer is now open. Watch out because it contains deadly red spiders! Fight them to get the rare red spider eggs, which when taken with other ingrediants to the Apothecary in varrock can be used to make a 4-dose strength potion!',
content:
'A new area at the end of the sewer is now open. Watch out because it contains deadly red spiders! Fight them to get the rare red spider eggs, which when taken with other ingrediants to the Apothecary in varrock can be used to make a 4-dose strength potion!',
date: '2001-02-05'
},
{
category_id: 7,
title: 'RuneScape now works on NetScape 6',
content: 'I finally worked out why netscape-6 wouldn\'t run RuneScape, and have now fixed the problem! The game will probably also now work properly on a few other browsers.',
content: "I finally worked out why netscape-6 wouldn't run RuneScape, and have now fixed the problem! The game will probably also now work properly on a few other browsers.",
date: '2001-02-07'
},
{
category_id: 7,
title: 'More RuneScape updates',
content:
`Firstly I have fixed the error which enabled you to make double doors vanish by lighting a fire next to them. So hopefully no more trapped people.
content: `Firstly I have fixed the error which enabled you to make double doors vanish by lighting a fire next to them. So hopefully no more trapped people.
<p>
I've also modified the trading window, so that you can point at an object someone is offering you to find out the objects name and description. This will help make trading fairer and safer.
<p>
@ -292,8 +295,7 @@ The 'champions guild' has been added south-west of Varrock. Only Adventurers who
{
category_id: 7,
title: 'New quest, and more fixes',
content:
`Little imps have invaded RuneScape and have been stealing anything which isn't nailed down! It's mostly nothing important, but they have also stolen 4 magic beads from Wizard Mizgog, and he wants them back. Go and talk to him in the wizards tower to get started on this quest.
content: `Little imps have invaded RuneScape and have been stealing anything which isn't nailed down! It's mostly nothing important, but they have also stolen 4 magic beads from Wizard Mizgog, and he wants them back. Go and talk to him in the wizards tower to get started on this quest.
<p>
The trading feature has been improved further. You can no longer accept an offer at the same time as the other player is changing it. This will hopefully stop the cheating and make trading with other players safer.
<p>
@ -303,8 +305,7 @@ A few other minor fixes have been made, for instance you can no longer walk thro
{
category_id: 7,
title: 'Yet another new quest, and new options menu',
content:
`Prince Ali of Al Kharid is being held captive by the Lady Keli. If you think you're up to a daring rescue mission then go to Al Kharid and talk to Chancellor Hassan. This is a long quest with many parts! Many thanks to Rab for the many hours he spent creating it, and to Paul for configuring it for use in the game.
content: `Prince Ali of Al Kharid is being held captive by the Lady Keli. If you think you're up to a daring rescue mission then go to Al Kharid and talk to Chancellor Hassan. This is a long quest with many parts! Many thanks to Rab for the many hours he spent creating it, and to Paul for configuring it for use in the game.
<p>
I've been working on a new options menu, to allow you to configure the game to your liking. So far there are only 2 options. The 1st option allows you to choose the behaviour of the camera, the new camera mode is the first step in a new system I am developing, which will ultimately make RuneScape run MUCH faster for people with slow computers. The 2nd option is to switch your character between PK and non-PK. (you can only switch twice, so choose carefully). I will be adding more options to the menu as required.
<p>
@ -315,8 +316,7 @@ I've also been working on a system to support clans/friend-lists. This isn't rea
{
category_id: 7,
title: 'Problems fixed',
content:
`I've fixed a few minor problems with the new quest. In case you don't already know getting the skin paste now works, and I've just solved the clay shortage problem.
content: `I've fixed a few minor problems with the new quest. In case you don't already know getting the skin paste now works, and I've just solved the clay shortage problem.
<p>
I'd also like to apologise for the slow speed of this site on the 28th-Feb. This problem has now been resolved and we're back to full speed again. I've also ordered a 2nd dedicated server to increase the reliability and speed of RuneScape in the future.`,
date: '2001-03-02'
@ -324,14 +324,14 @@ I'd also like to apologise for the slow speed of this site on the 28th-Feb. This
{
category_id: 7,
title: 'Second server online',
content: 'I\'ve been busy this last week setting up a new server, and modifying RuneScape to use it. I\'m pleased to announce that RuneScape is now running using two dedicated servers. This should allow more simultaneous players, and make the game more reliable at peak times.',
content:
"I've been busy this last week setting up a new server, and modifying RuneScape to use it. I'm pleased to announce that RuneScape is now running using two dedicated servers. This should allow more simultaneous players, and make the game more reliable at peak times.",
date: '2001-03-10'
},
{
category_id: 7,
title: 'This weeks update',
content:
`I've just uploaded this weeks update. I didn't get as many features added as I hoped, because I instead had to spend time modifing the system to stop a few players intent on spoiling the game for everyone else. Players who try to break the rules are just slowing down the improvements to the game, so please don't do it! Luckily Paul has also made lots of improvements this week, so between us we've still done quite a lot of new stuff.
content: `I've just uploaded this weeks update. I didn't get as many features added as I hoped, because I instead had to spend time modifing the system to stop a few players intent on spoiling the game for everyone else. Players who try to break the rules are just slowing down the improvements to the game, so please don't do it! Luckily Paul has also made lots of improvements this week, so between us we've still done quite a lot of new stuff.
<p>
This weeks updates are:
@ -351,8 +351,7 @@ This weeks updates are:
{
category_id: 7,
title: 'Massive update!',
content:
`Asgarnia is online! and includes:
content: `Asgarnia is online! and includes:
<ul>
<li>Ice mountain</li>
<li>monastery with healing monks</li>
@ -385,8 +384,7 @@ In addition to this there is a new friends system, and new privacy features! Hav
{
category_id: 7,
title: 'Server problems',
content:
`We seem to be having a few server problems at the moment. This is due to the large amount of new code we added to the game yesterday. I'm working on fixing it though, so please be patient, and I'll get everything running smoothly as soon as possible.
content: `We seem to be having a few server problems at the moment. This is due to the large amount of new code we added to the game yesterday. I'm working on fixing it though, so please be patient, and I'll get everything running smoothly as soon as possible.
<p>
The good news is that I think I have just resolved the server down-time issues, so hopefully the server won't go offline completely anymore. There is still an issue with certain players having problems when more than 1024 people play at once, this is only a problem at peak times and I expect to fix it on Sunday evening.
<p>
@ -396,8 +394,7 @@ Also I expect to get the message boards, current-players, and password-emailing
{
category_id: 7,
title: 'Server fixed + message boards online',
content:
`I have now fixed the majority of the problems with the new server. All the major problems are now sorted, and I'm now working on a few less critical issues. I'm also going to update the world-map as soon as possible so you can find your way around Asgarnia.
content: `I have now fixed the majority of the problems with the new server. All the major problems are now sorted, and I'm now working on a few less critical issues. I'm also going to update the world-map as soon as possible so you can find your way around Asgarnia.
<p>
More good news is that I have just put the message boards back-online. I have taken the chance to redesign and improve them, so I hope you like the new design!`,
date: '2001-04-10'
@ -405,14 +402,13 @@ More good news is that I have just put the message boards back-online. I have ta
{
category_id: 7,
title: 'Asgarnia map online',
content: 'The updated world map is now online, and I\'ve labelled all the key locations in Asgarnia, so you can now find your way around without getting lost!',
content: "The updated world map is now online, and I've labelled all the key locations in Asgarnia, so you can now find your way around without getting lost!",
date: '2001-04-11'
},
{
category_id: 7,
title: 'Server status page, my-friends, and password recovery pages',
content:
`The 'current players' page was getting too long so it's been replaced with a new 'server status' page which tells you the number of people currently playing, and lists the top players who are logged in. So you can still tell which of your friends are playing I've also added a new 'my-friends' page which tells you if the people on your friends list are online or not.
content: `The 'current players' page was getting too long so it's been replaced with a new 'server status' page which tells you the number of people currently playing, and lists the top players who are logged in. So you can still tell which of your friends are playing I've also added a new 'my-friends' page which tells you if the people on your friends list are online or not.
<p>
I've also got the lost password recovery feature working again. It's now available by clicking 'lost password' on the menu to the left and entering your account details.`,
date: '2001-04-13'
@ -421,8 +417,7 @@ I've also got the lost password recovery feature working again. It's now availab
{
category_id: 7,
title: 'Bugs fixed',
content:
`I've been busy fixing problems with Runescape, I'm pleased to announce that all the following issues are now fixed:
content: `I've been busy fixing problems with Runescape, I'm pleased to announce that all the following issues are now fixed:
<ul>
<li>Players were sometimes seen appearing in random places, (e.g on top of your own character, or in the sea etc...)</li>
@ -436,8 +431,7 @@ I've also got the lost password recovery feature working again. It's now availab
{
category_id: 7,
title: 'New RuneScape windows client!',
content:
`Get into Runescape more quickly without having to wait for it to load every time, with our new windows version!
content: `Get into Runescape more quickly without having to wait for it to load every time, with our new windows version!
<p>
When run, the program automatically checks our main server for the latest updates then starts the game from your harddisk. It's only 116k so download it now!`,
date: '2001-04-25'
@ -445,8 +439,7 @@ When run, the program automatically checks our main server for the latest update
{
category_id: 7,
title: 'Improved windows client',
content:
`It seems the new windows client didn't work for everyone, so I've been worked hard to fix it and make it more compatible. Version 2 is now available for download, and fixes the following problems:
content: `It seems the new windows client didn't work for everyone, so I've been worked hard to fix it and make it more compatible. Version 2 is now available for download, and fixes the following problems:
<ul>
<li>IE-5.5 users were hearing "beep" noises whenever they pressed a key!</li>
@ -459,8 +452,7 @@ When run, the program automatically checks our main server for the latest update
{
category_id: 7,
title: 'Item stealing fixed',
content:
`Due to popular request I have now modified the game so that other players can't steal the items you get when you kill another player or monster.
content: `Due to popular request I have now modified the game so that other players can't steal the items you get when you kill another player or monster.
<p>
After killing a monster/player you now have 1 minute to choose the items you want. After 1 minute any remaining items will become available to other players too.`,
date: '2001-05-01'
@ -468,8 +460,7 @@ After killing a monster/player you now have 1 minute to choose the items you wan
{
category_id: 7,
title: 'Improved user interface',
content:
`I've changed the way the controls work, to make controlling your character in crowded areas much easier. After putting the original improvement online it became apparent that further alterations were necessary so I've ended up making minor alterations repeatedly over the past few days.
content: `I've changed the way the controls work, to make controlling your character in crowded areas much easier. After putting the original improvement online it became apparent that further alterations were necessary so I've ended up making minor alterations repeatedly over the past few days.
<p>
<b>The final resulting interface now works as follows:</b>
<p>
@ -497,8 +488,7 @@ Just remember: left-button=action, right-button=menu
{
category_id: 7,
title: '3rd Runescape server on the way',
content:
`Runescape is still growing massively in popularity. There are now so many players at peak times that I've just ordered a 3rd dedicated server to cope, and I hope to receive it within a few days time.
content: `Runescape is still growing massively in popularity. There are now so many players at peak times that I've just ordered a 3rd dedicated server to cope, and I hope to receive it within a few days time.
<p>
The new server will be used to run a 2nd copy of the runescape world to reduce the overcrowding. The 2nd world will be exactly the same as the current one, and so will effectively allow twice as many people to play Runescape at once.
<p>
@ -508,8 +498,7 @@ When logging in you will be able to choose which server to play on, and will sti
{
category_id: 7,
title: 'RuneScape updated',
content:
`I've just updated runescape to add the following features:
content: `I've just updated runescape to add the following features:
<ul>
<li>Better smithing tables, with more even spacing of items</li>
<li>New goblin quest - help the goblins find a new look for their armour!</li>
@ -521,14 +510,14 @@ I have also just received the 3rd server I promised, I am now configuring it, an
{
category_id: 7,
title: '3rd runescape server online',
content: 'The new runescape server is online! I was planning on testing it a bit more and launching it tommorow, but so many people are trying to play tonight that the system couldn\'t cope so I\'ve been forced to launch it early. Hopefully everyone who wants to play will be able to now.',
content:
"The new runescape server is online! I was planning on testing it a bit more and launching it tommorow, but so many people are trying to play tonight that the system couldn't cope so I've been forced to launch it early. Hopefully everyone who wants to play will be able to now.",
date: '2001-05-09 01:30:00'
},
{
category_id: 7,
title: 'Lots of small improvements',
content:
`I've been busy making lots of minor alterations and bugfixes to the game. The main changes are:
content: `I've been busy making lots of minor alterations and bugfixes to the game. The main changes are:
<ul>
<li>My-friends webpage now works properly, and also shows which server they are on</li>
<li>You can now private message friends even if they are playing on the other server</li>
@ -548,8 +537,7 @@ I have also just received the 3rd server I promised, I am now configuring it, an
{
category_id: 7,
title: 'Updated FAQ',
content:
`I've updated the frequently-asked-questions page to answer some more common queries. Click here to view it.
content: `I've updated the frequently-asked-questions page to answer some more common queries. Click here to view it.
<p>
If you think there are further questions missing from the FAQ then let me know what you think I should add.`,
date: '2001-05-21'
@ -557,20 +545,21 @@ If you think there are further questions missing from the FAQ then let me know w
{
category_id: 7,
title: '4th RuneScape server ordered',
content: 'Last night saw over 2000 simultaneous RuneScape players for the first time ever. Hurrah! To keep up with the increasing number of players I\'ve just ordered yet another server to help keep the game running smoothly. I hope to have it within a few days.',
content:
"Last night saw over 2000 simultaneous RuneScape players for the first time ever. Hurrah! To keep up with the increasing number of players I've just ordered yet another server to help keep the game running smoothly. I hope to have it within a few days.",
date: '2001-05-24'
},
{
category_id: 7,
title: 'Pop up windows removed',
content: 'I\'ve removed the pop up windows because there were just too annoying. Hopefully the remaining adverts will make enough money that I don\'t have to put them back (E.g the revenue from banner-advert clicks doesn\'t fall any further)',
content:
"I've removed the pop up windows because there were just too annoying. Hopefully the remaining adverts will make enough money that I don't have to put them back (E.g the revenue from banner-advert clicks doesn't fall any further)",
date: '2001-05-24'
},
{
category_id: 7,
title: 'Runescape updated',
content:
`Due to popular request, female players can now go and talk to one of the characters in the game, and get their armour modified to look more feminine.
content: `Due to popular request, female players can now go and talk to one of the characters in the game, and get their armour modified to look more feminine.
<p>
The ice dungeon has been expanded and improved. It also now includes a new ice-warrior monster.
<p>
@ -580,8 +569,7 @@ The server status page now shows the top crafter, top magician, and top cleric.`
{
category_id: 7,
title: 'New magic system online!',
content:
`The RuneScape magic system has been massively improved with the additon of lots of new spells. Magic can also now be used during combat! In addition to this there is also a new prayer system which lets you temporarily cast various effects on your character. This means there is now a total of 42 spells and prayers to choose from.
content: `The RuneScape magic system has been massively improved with the additon of lots of new spells. Magic can also now be used during combat! In addition to this there is also a new prayer system which lets you temporarily cast various effects on your character. This means there is now a total of 42 spells and prayers to choose from.
<p>
For more information on the new systems click below:
Guide to magic
@ -591,8 +579,7 @@ Guide to prayers`,
{
category_id: 7,
title: '4th Runescape server online',
content:
`The 4th runescape server is now online! (this means we now have 3 game servers, and 1 web server). It has now been fully connected so you can use it with both the windows and online versions of the game
content: `The 4th runescape server is now online! (this means we now have 3 game servers, and 1 web server). It has now been fully connected so you can use it with both the windows and online versions of the game
<p>
I have also modifed the game to prevent people from logging in multiple players at once from the same machine, because doing so is against the rules! If you have problems with the system seeing your home-network as a single machine, then try using the window client which is better at detecting your network configuration.<br>
<b>Update</b>: Even if you can't use the windows client then up to 3 people on the same home-network can still play at once, as long as each player uses a different server.`,
@ -601,13 +588,14 @@ I have also modifed the game to prevent people from logging in multiple players
{
category_id: 7,
title: 'Prayers last 50% longer',
content: 'It seems most people thought they prayers weren\'t quite powerful enough. I\'ve therefore now changed the game so that all the prayers last 50% longer, before you need to recharge.',
content: "It seems most people thought they prayers weren't quite powerful enough. I've therefore now changed the game so that all the prayers last 50% longer, before you need to recharge.",
date: '2001-05-26'
},
{
category_id: 7,
title: 'Sorry for recent server problems',
content: 'Any regular runescape players will have noticed that the servers have been experiencing problems over the weekend. I have been working extremely hard to try and resolve this problem, and hopefully everything will be back to normal now. I believe it was caused by a memory leak in the new magic system which I added on friday. I have now fixed the memory leak so hopefully the server will now be stable. I apologise for any inconveniance caused. - Andrew',
content:
'Any regular runescape players will have noticed that the servers have been experiencing problems over the weekend. I have been working extremely hard to try and resolve this problem, and hopefully everything will be back to normal now. I believe it was caused by a memory leak in the new magic system which I added on friday. I have now fixed the memory leak so hopefully the server will now be stable. I apologise for any inconveniance caused. - Andrew',
date: '2001-05-27'
},
{
@ -619,8 +607,7 @@ I have also modifed the game to prevent people from logging in multiple players
{
category_id: 7,
title: 'Sorry for recent server problems',
content:
`I've added a whole new skill for you to try in RuneScape! There are various fishing points scattered around the rivers and ocean. Go to the fishing-shop in Port Sarim to get the equipment you need and then try your hand at fishing. There are 10 different fish to catch, and 5 different fishing techniques to try!
content: `I've added a whole new skill for you to try in RuneScape! There are various fishing points scattered around the rivers and ocean. Go to the fishing-shop in Port Sarim to get the equipment you need and then try your hand at fishing. There are 10 different fish to catch, and 5 different fishing techniques to try!
<p>
As well as the fish, there are now even more things to cook and eat in RuneScape. If you have a sufficiently high cooking level you can now make pizzas, stew and cakes! Finally I've changed some of the level requirements and the healing properties of some of the foods to be more evenly balanced.
<p>
@ -630,14 +617,14 @@ For more information on fishing and cooking Click here`,
{
category_id: 7,
title: 'The island of Karamja is open',
content: 'Paul has now finished the island of Karamja! Go and talk to Captain Tobias at Port Sarim if you want a boat trip to the new island. Karamja also features a brand new quest in which you must hunt for hidden treasure! To start the new quest go and talk to Redbeard Frank at Port Sarim.',
content:
'Paul has now finished the island of Karamja! Go and talk to Captain Tobias at Port Sarim if you want a boat trip to the new island. Karamja also features a brand new quest in which you must hunt for hidden treasure! To start the new quest go and talk to Redbeard Frank at Port Sarim.',
date: '2001-06-11'
},
{
category_id: 7,
title: 'Lots of minor fixes and tweaks',
content:
`Just made a quick update to fix and tweak various small problems. This update fixes the following:
content: `Just made a quick update to fix and tweak various small problems. This update fixes the following:
<ul>
<li>There are now even more customs officers on karamja</li>
<li>There is more fishing bait available in the shop and as monster drops</li>
@ -653,14 +640,13 @@ For more information on fishing and cooking Click here`,
{
category_id: 7,
title: 'Updated world map',
content: 'I\'ve updated the world-map. It now shows the location of the jewellery shop, gem shop, crafting shop, armour conversion shop, fishing shop, and the places where you can catch fish.',
content: "I've updated the world-map. It now shows the location of the jewellery shop, gem shop, crafting shop, armour conversion shop, fishing shop, and the places where you can catch fish.",
date: '2001-06-12'
},
{
category_id: 7,
title: 'New password recovery system',
content:
`I've changed the system for retrieving a lost password. The new system doesn't use email because not everyone has a secure email account they can use, and people's email address often changes.
content: `I've changed the system for retrieving a lost password. The new system doesn't use email because not everyone has a secure email account they can use, and people's email address often changes.
<p>
Next time you login you will be asked for the answers to 5 security questions. Then if you should lose your password you can use these answers to set a new password on your account. You will only be allowed to set the questions once, after which they are permanently fixed. Therefore you should be careful to choose questions+answers which other people won't be able to guess, but which you will easily remember.`,
date: '2001-06-20'
@ -668,8 +654,7 @@ Next time you login you will be asked for the answers to 5 security questions. T
{
category_id: 7,
title: 'More runescape bugs fixed',
content:
`I have now fixed the following bugs with runescape:
content: `I have now fixed the following bugs with runescape:
<ul>
<li>improved password box to allow up to 20 characters</li>
<li>fixed it so you can still stack extra runes/arrows even when your inventory is full.</li>
@ -686,7 +671,8 @@ Next time you login you will be asked for the answers to 5 security questions. T
{
category_id: 7,
title: 'New tailoring ability',
content: 'Paul has extended the crafting skill so you can now also use it make items out of leather. Take a cow hide to the tannery to get some leather. You can then use the leather with some thread to make gloves, boots and leather armour.',
content:
'Paul has extended the crafting skill so you can now also use it make items out of leather. Take a cow hide to the tannery to get some leather. You can then use the leather with some thread to make gloves, boots and leather armour.',
date: '2001-06-23'
},
{
@ -698,8 +684,7 @@ Next time you login you will be asked for the answers to 5 security questions. T
{
category_id: 7,
title: 'Improved prayer system and more!',
content:
`<ul>
content: `<ul>
<li>New monastery area which you can enter if you are a high level cleric. Includes monks robes and a special new altar to pray at.</li>
<li>Silver rocks to mine (requires level 20 mining)</li>
<li>Holy symbol of Saradomin, made by crafting from silver. Take it to the new monastery area to get it blessed to make your prayers last longer</li>
@ -710,9 +695,8 @@ Next time you login you will be asked for the answers to 5 security questions. T
},
{
category_id: 7,
title: 'Andrew\'s guide to not losing your password',
content:
`An increasing number of people are trying to trick other players into giving away their password. Please use a bit of common sense to avoid getting tricked in this way. Some common tricks to avoid are below:
title: "Andrew's guide to not losing your password",
content: `An increasing number of people are trying to trick other players into giving away their password. Please use a bit of common sense to avoid getting tricked in this way. Some common tricks to avoid are below:
<ul>
<li>Do NOT enter your password into ANY website other than runescape.com! If you go somewhere else and see a chat room or survey or cheat site which claims it needs your password then that site is a trick and will steal your pass. Don't be a fool. Be very suspicious of anything or anyone that asks you for your password.</li>
<li>Rememeber that real Jagex staff will NEVER EVER ask you for your password! If anyone talks to you, or emails you claiming to be me, and asks for your password, then they are lieing and are trying to steal your account. Jagex staff will NEVER ask you for your password for any reason!</li>
@ -724,8 +708,7 @@ I'm not trying to scare you, all the points above are just common sense! If some
{
category_id: 7,
title: '5th RuneScape server online',
content:
`I just finished setting up another server help run RuneScape. The new server isn't being used to run another parallel world, but is instead working behind the scenes to help manage the save-games.
content: `I just finished setting up another server help run RuneScape. The new server isn't being used to run another parallel world, but is instead working behind the scenes to help manage the save-games.
<p>
This gives a total of 3 world servers, and 2 support servers. This should hopefully increase the total system capacity to 4000 players at once.`,
date: '2001-07-17'
@ -733,8 +716,7 @@ This gives a total of 3 world servers, and 2 support servers. This should hopefu
{
category_id: 7,
title: 'Runescape updated',
content:
`The bank has been upgraded so you can now store any item in the bank not just gold. Useful for keeping your spare weapons, gems, and anything else you might want to store. You can store up to 48 different items, with a maximum limit of 16 million items per stack.
content: `The bank has been upgraded so you can now store any item in the bank not just gold. Useful for keeping your spare weapons, gems, and anything else you might want to store. You can store up to 48 different items, with a maximum limit of 16 million items per stack.
<p>
Runite items have been added to the game! Runite is the most powerful metal in the game, better than even adamantite. Both rune weapons and rune armour are now available to purchase from the champions guild. (hint - there is an extra item only available as a rare monster drop from a particularly strong monster!)
<p>
@ -746,8 +728,7 @@ The player owned houses have been removed. This is because since they were origi
category_id: 7,
title: 'Wilderness system online',
// this one has 2 pictures embedded
content:
`The new wilderness area of runescape is online! This is a huge new area which is part of an improved player-combat system which was first proposed a few months ago. A poll on the website at the time showed the vast majority of players in favour of the new system, so we have now written it.
content: `The new wilderness area of runescape is online! This is a huge new area which is part of an improved player-combat system which was first proposed a few months ago. A poll on the website at the time showed the vast majority of players in favour of the new system, so we have now written it.
<p>
In the improved system player fighting is only allowed in the new wilderness area. The ability to switch between PK and Non-PK will be removed and instead you just go into the dangerous wilderness area if you want to fight other players, or stay within the current map area if you don't.
<p>
@ -757,14 +738,14 @@ The further you go into the wilderness the more dangerous it becomes, but the mo
{
category_id: 7,
title: 'Stats menu now shows XP',
content: 'Due to a huge number of requests, the stats menu now shows you the exact number of experiance points you have in each skill, and the number you need to reach to advance a level. For the more detailed information about a particular stat just point your mouse over it, and the numbers will be shown at the bottom of the menu.',
content:
'Due to a huge number of requests, the stats menu now shows you the exact number of experiance points you have in each skill, and the number you need to reach to advance a level. For the more detailed information about a particular stat just point your mouse over it, and the numbers will be shown at the bottom of the menu.',
date: '2001-08-13'
},
{
category_id: 7,
title: 'A few bugs fixed',
content:
`I just fixed a couple of bugs with the new systems. I've fixed a problem with the wilderness, whereby low level archers were able to attack high level players in the wilderness without being attackable back! And I've fixed a problem with the new stats menu where the XP indicator didn't update properly.
content: `I just fixed a couple of bugs with the new systems. I've fixed a problem with the wilderness, whereby low level archers were able to attack high level players in the wilderness without being attackable back! And I've fixed a problem with the new stats menu where the XP indicator didn't update properly.
<p>
Also bear in mind that we might tweak the wilderness a bit over the next few days if it proves to be unbalanced, although everything seem ok so far. We are also planning on adding a dualing system which will allow combat outside the wilderness if both players agree to it.`,
date: '2001-08-13'
@ -772,14 +753,14 @@ Also bear in mind that we might tweak the wilderness a bit over the next few day
{
category_id: 7,
title: 'Website updated',
content: 'I have just updated the worldmap. It now shows the mining guild and the very south edge of the wilderness. The whole wilderness isn\'t shown however, so you\'ll have to explore it for yourselves! I\'ve also added 4 new message boards, to give a total of 8, because the old boards were getting extremely crowded. Enjoy!',
content:
"I have just updated the worldmap. It now shows the mining guild and the very south edge of the wilderness. The whole wilderness isn't shown however, so you'll have to explore it for yourselves! I've also added 4 new message boards, to give a total of 8, because the old boards were getting extremely crowded. Enjoy!",
date: '2001-08-15'
},
{
category_id: 7,
title: 'Lots of small improvements',
content:
`<ul>
content: `<ul>
<li>I've fixed the kill stealing problem, the treasure now goes to whoever damaged the monster most overall rather than the person who got the last hit. XP is split proportionally between all people who helped kill the monster..</li>
<li>Teleport spells no longer work once you reach level-20 wilderness or beyond. This makes the deep part of the wilderness dangerous even for high level players..</li>
<li>People who are green on your friends list also appear as green dots on the mini-map, making it easier to find them in the game, and easier to go adventuring with a group of friends..</li>
@ -799,8 +780,7 @@ Also bear in mind that we might tweak the wilderness a bit over the next few day
category_id: 7,
title: 'Dragon quest online!',
// this one has 1 picture embedded
content:
`Think you've got what it takes to slay a dragon? If so try our new quest! This quest has been designed with the more experienced runescape players in mind so it's fairly tricky.
content: `Think you've got what it takes to slay a dragon? If so try our new quest! This quest has been designed with the more experienced runescape players in mind so it's fairly tricky.
<p>
To start the quest go and talk to the guildmaster in the champions guild. From there you will have to solve a series of puzzles before travelling to slay the dragon itself. Only heros who have proven themselves worthy by completing this quest can purchase and wear the best armour in the game. The new rune platemail body!`,
date: '2001-09-23'
@ -808,8 +788,7 @@ To start the quest go and talk to the guildmaster in the champions guild. From t
{
category_id: 7,
title: 'Optional RuneScape members service coming soon!',
content:
`Due to much demand we are planning on launching an optional new premium RuneScape service soon. This will bring a whole load of great benefits such as: frequent updates, NO-adverts, good customer support, no scammers/cheaters (we'll kick 'em out), and much more! We're hoping to make it available for just $5 (us-dollars) a month and this tiny amount of money will pay for many great RuneScape updates to come, and will ensure we can keep running RuneScape for YOU.
content: `Due to much demand we are planning on launching an optional new premium RuneScape service soon. This will bring a whole load of great benefits such as: frequent updates, NO-adverts, good customer support, no scammers/cheaters (we'll kick 'em out), and much more! We're hoping to make it available for just $5 (us-dollars) a month and this tiny amount of money will pay for many great RuneScape updates to come, and will ensure we can keep running RuneScape for YOU.
<p>
This is great news, because it will enable us to offer you more frequent RuneScape updates. Some of the first updates we have planned include: A duelling system, more monsters (wolf, giant-bat, lizard-man etc..), lots of new items, sound effects, more quests, and there will be loads more stuff after this.
<p>
@ -832,8 +811,7 @@ Andrew Gower`,
{
category_id: 7,
title: '27th November',
content:
`I've added a confirm-box to the trade window to make scamming much more difficult. After both players have selected objects, an extra confirm-box showing the details of the trade is displayed. This allows you to check all the details are correct before parting with your items.
content: `I've added a confirm-box to the trade window to make scamming much more difficult. After both players have selected objects, an extra confirm-box showing the details of the trade is displayed. This allows you to check all the details are correct before parting with your items.
<p>
If a player on your friends list is shown in yellow (i.e they are on a different server), you can now find out which server they are on, by moving your mouse pointer over their name.
<p>
@ -845,8 +823,7 @@ I've fixed another bug with the chat filter which was making it over sensitive i
{
category_id: 7,
title: '30th April',
content:
`New members update online! Many RPGs have a thieving/rogue skill and this is something which until now has been missing from RuneScape.
content: `New members update online! Many RPGs have a thieving/rogue skill and this is something which until now has been missing from RuneScape.
<p>
We've now added this popular skill to the members version and it includes abilities such as pick-pocketing, picking locks, disarming traps, and stealing from market stalls. For more information about the new thieiving skill please refer to the online manual.
<p>
@ -856,8 +833,7 @@ In addition we have added a quest which uses the new skill. Can you help the rig
{
category_id: 7,
title: '8th May',
content:
`Both members and free-edition runescape have been updated with new content.
content: `Both members and free-edition runescape have been updated with new content.
<p>
First up the members wilderness has been expanded to include: a new level-56 wilderness area, scorpion ravine, pirate hall, rogues to pickpocket, doors to lockpick, high level wilderness dungeon, shadow-spider monster, fire-giant monster.
<p>
@ -871,8 +847,7 @@ Both members and free versions have been updated with a new 'make-over mage' cha
{
category_id: 7,
title: '28th May',
content:
`Two new members quests are now online.
content: `Two new members quests are now online.
<p>
In the first quest you must help the dwarves win a fishing competition. To start the quest look for the passage way at east kandarin.
<p>
@ -882,8 +857,7 @@ In the second quest you must carry out a number of tasks for the monks south of
{
category_id: 7,
title: '29th May',
content:
`Massive crackdown on cheating players.
content: `Massive crackdown on cheating players.
<p>
As part of our promised commitment to stamping down and stopping cheats in RuneScape we have developed a new system to automatically detect people using macro/bot software to gain stats unfairly.
<p>
@ -903,8 +877,7 @@ Click here For a complete list of all the people who have already been stat-wipe
{
category_id: 7,
title: '10th July',
content:
`Today Runescape update fixes a large number of outstanding bugs in the game.
content: `Today Runescape update fixes a large number of outstanding bugs in the game.
<p>
The following issues have been fixed:
<ul>
@ -922,8 +895,7 @@ The following issues have been fixed:
{
category_id: 7,
title: '12th July',
content:
`More ways of becoming a RuneScape member are now available!
content: `More ways of becoming a RuneScape member are now available!
In addition to the usual credit/debit-card we've teamed up with paybycash.com to offer the following additional payment options:
<p>
<ul>
@ -943,8 +915,7 @@ Click here to sign up for RuneScape members now!`,
{
category_id: 7,
title: '18th July',
content:
`I went away for a short holiday on saturday, and it seems Runescape promptly broke in my absence. It seems I can't even take 1 week a year away from running this game! I'd like to apologise for the people who have experienced connections problems over the last few days. I've decided to abort the remainder of my holiday and have come back to the office to fix RuneScape instead.
content: `I went away for a short holiday on saturday, and it seems Runescape promptly broke in my absence. It seems I can't even take 1 week a year away from running this game! I'd like to apologise for the people who have experienced connections problems over the last few days. I've decided to abort the remainder of my holiday and have come back to the office to fix RuneScape instead.
<p>
I'm currently transfering the database to a new server which is 6 times faster than the old one which should hopefully get things running smoothly again. Once again sorry things weren't working 100%. Now I'm back at work I should be able to get it working smoothly fairly quickly.
<p>
@ -954,20 +925,21 @@ Andrew`,
{
category_id: 7,
title: '19th July',
content: 'The RuneScape database is now running from a faster server so everything should be a lot more reliable now. I have also adjusted the network settings of the New-York servers so hopefully they are a lot less laggy now. If you live in the US then you should try them out again.',
content:
'The RuneScape database is now running from a faster server so everything should be a lot more reliable now. I have also adjusted the network settings of the New-York servers so hopefully they are a lot less laggy now. If you live in the US then you should try them out again.',
date: '2002-07-19'
},
{
category_id: 7,
title: '20th July',
content: 'Our new improved customer support system is finally online. I\'ve completely rewritten the system to be easier and quicker to use. (and a lot more reliable!) If you\'ve been having trouble submitting support queries recently the new system should work properly for you.',
content:
"Our new improved customer support system is finally online. I've completely rewritten the system to be easier and quicker to use. (and a lot more reliable!) If you've been having trouble submitting support queries recently the new system should work properly for you.",
date: '2002-07-20'
},
{
category_id: 7,
title: '23rd July',
content:
`We've updated RuneScape with 3 new members quests!
content: `We've updated RuneScape with 3 new members quests!
<p>
King arthur is sending out his knights on a quest for the famous holy grail. If you are a knight of the round table go to king arthur for further orders
<p>
@ -981,8 +953,7 @@ We've also added a variety of other features including lots of new areas for the
{
category_id: 7,
title: '26th July',
content:
`The hiscore tables are back online, and we've increased them to show the top 100,000 players!! We've also added a search facility so you can quickly find your name and see where you rank
content: `The hiscore tables are back online, and we've increased them to show the top 100,000 players!! We've also added a search facility so you can quickly find your name and see where you rank
<p>
An unrelated warning to free edition users - we've discovered that the members 'gnome amulet' is accidently usable on the free server. However we will obviously be fixing this oversight in the next update, so we'd like to warn you NOT to waste your money buying an object which will become useless to you in the next update!`,
date: '2002-07-26'
@ -990,8 +961,7 @@ An unrelated warning to free edition users - we've discovered that the members '
{
category_id: 7,
title: '15th August',
content:
`We've just got another 2 members quests online. Sorry it took a bit longer than expected. We've been trying a new testing method to make sure there are less bugs than the last update.
content: `We've just got another 2 members quests online. Sorry it took a bit longer than expected. We've been trying a new testing method to make sure there are less bugs than the last update.
<p>
The 1st quest is the hazeel cult quest. Thieves have stolen the carnillean family armour, and you are asked to recover it. This quest is a bit different because there are two very different ways of completing it! Will you side with the Hazeel Cult or with Ceril Carnillean?
<p>
@ -1001,8 +971,7 @@ The 2nd quest is the sheep herder quest in which you must safely herd four plagu
{
category_id: 7,
title: '26th August',
content:
`RuneScape progress update.
content: `RuneScape progress update.
<p>
It's been a while since I last revealed what we're currently working on, so here goes!
<p>
@ -1020,8 +989,7 @@ Andrew`,
{
category_id: 7,
title: '27th August',
content:
`Epsiode 1 of our new multipart quest is now online. Edmond's daugher Elena has gone missing in West Ardougne whilst trying to help the plague victims there. See if you can find out whats going on. The story started in this quest will be continued in future updates.
content: `Epsiode 1 of our new multipart quest is now online. Edmond's daugher Elena has gone missing in West Ardougne whilst trying to help the plague victims there. See if you can find out whats going on. The story started in this quest will be continued in future updates.
<p>
We have also added a new Ardougne teleport spell for high level magicians, and a new bank in East Ardougne.`,
date: '2002-08-27'
@ -1029,8 +997,7 @@ We have also added a new Ardougne teleport spell for high level magicians, and a
{
category_id: 7,
title: '9th September',
content:
`New members quest online - The sea slug quest
content: `New members quest online - The sea slug quest
<p>
Something mysterious is happening on the new offshore fishing platform. People who've gone on their regular fishing trip just haven't returned! Can you rescue the missing father and son from the unknown horrors out at sea?
<p>
@ -1040,8 +1007,7 @@ We've also added the ablitity to catch oysters whilst fishing with a bignet. The
{
category_id: 7,
title: '11th September',
content:
`New abuse reporting system online!
content: `New abuse reporting system online!
<p>
We've added a new 'abuse report' button to the RuneScape game which sends an untamperable snapshot of recent chat/trade events directly to customer support.
<p>
@ -1055,8 +1021,7 @@ Thanks Andrew`,
{
category_id: 7,
title: '19th September',
content:
`We're still working on the big graphical update. It sure is a lot of work! It's still a way off, so meanwhile we're going to show occasional previews of how things are progressing.
content: `We're still working on the big graphical update. It sure is a lot of work! It's still a way off, so meanwhile we're going to show occasional previews of how things are progressing.
<p>
As well as improving all the graphics, we're also reworking all the animation to use a lot more frames so everything moves more smoothly. Here is a preview of how some of the monsters/people look using our new animation system.
<p>
@ -1066,8 +1031,7 @@ Remember: Don't be fooled by fake websites, offering to let you test the new ver
{
category_id: 7,
title: '24th September',
content:
`RuneScape was updated today with changes big and small. As well as fixing lots of small issues, we've also added the following features:
content: `RuneScape was updated today with changes big and small. As well as fixing lots of small issues, we've also added the following features:
<p>
Tutorial Island - All newcomers can now take heart! First-time players will now start their adventures on tutorial island, where the good people of Runescape are busy preparing people for the challenges that await them. Learn the way of the rune, and pick up some handy culinary tips.
<p>
@ -1079,15 +1043,15 @@ King Black Dragon (Members monster) - The most fearsome monster ever to venture
{
category_id: 7,
title: '25th September',
content: 'We\'ve updated the RuneScape world-map to show all the latests areas and additions to the map. Newly mapped areas include West Ardougne, and the Gnome Tree Village. The map is pretty huge now so use the scrollbars to scroll around and view the entire area.',
content:
"We've updated the RuneScape world-map to show all the latests areas and additions to the map. Newly mapped areas include West Ardougne, and the Gnome Tree Village. The map is pretty huge now so use the scrollbars to scroll around and view the entire area.",
date: '2002-09-25'
},
// https://web.archive.org/web/20021204035847/http://www.runescape.com/Runescape.html
{
category_id: 7,
title: '23rd October',
content:
`The mother of all updates has just landed!
content: `The mother of all updates has just landed!
<p>
<b>New Quests:</b>
<p>
@ -1113,8 +1077,7 @@ Improved monster A.I. Monsters who are far weaker than you no longer auto attack
{
category_id: 7,
title: '6th November',
content:
`There is a small bug in varrock, so please be careful. It is possible to get stuck behind a fence in the south-east corner near the 'dancing donkey' inn. Please do NOT go there or you may become trapped.
content: `There is a small bug in varrock, so please be careful. It is possible to get stuck behind a fence in the south-east corner near the 'dancing donkey' inn. Please do NOT go there or you may become trapped.
<p>
If you are already trapped then it is possible to escape by clicking 'attack' on an npc on the opposite side of the fence, or by clicking 'follow' on a player on the opposite side of the fence. If you still can't get out then wait until monday when the builders are coming in to remove that fence.
<p>
@ -1124,8 +1087,7 @@ We apologise for this bug, it will be fixed on monday. Anyone trying to get othe
{
category_id: 7,
title: '11th November',
content:
`Server upgrades. This morning we upgraded our philadelphia servers. We've made the following improvements:
content: `Server upgrades. This morning we upgraded our philadelphia servers. We've made the following improvements:
<ul>
<li>All philadelphia servers moved to a new power circuit</ul>
<li>Our main database server has twice as much memory (it now has 2gig)</ul>
@ -1137,8 +1099,7 @@ We apologise for not fixing the bug mentioned below today. Unforseen problems wi
{
category_id: 7,
title: '13th November',
content:
`Mining improved! We have changed mining and fishing so that they no longer consist of hours of tedious repetative clicking. The rocks are now easier to mine so you don't have to click anything like as much, and prospecting always works.
content: `Mining improved! We have changed mining and fishing so that they no longer consist of hours of tedious repetative clicking. The rocks are now easier to mine so you don't have to click anything like as much, and prospecting always works.
<p>
Instead of the repetative clicking we have introduced a new 'fatigue' system which means as you do tasks such as mining your character slowly becomes tired. After a certain point (which depends on your level, and on the rock), you will become too tired to mine. When this happens simply rest at a bed or eat some food, and you'll feel much more lively again. The upshot of this is that the honest miners should be able to get their ore slightly quicker, whilst the cheats will just have to go somewhere else.
<p>
@ -1148,8 +1109,7 @@ Also due to popular request we have modified the wilderness monsters so they are
{
category_id: 7,
title: 'Fatigue - version #2 now online',
content:
`We've worked extremely hard today to update the mining/fishing system with respect to your comments and feedback. The old system was considered rather too much of a change by many, so we've made a new one is more like the original mining. Here is how the new system works.
content: `We've worked extremely hard today to update the mining/fishing system with respect to your comments and feedback. The old system was considered rather too much of a change by many, so we've made a new one is more like the original mining. Here is how the new system works.
<p>
<ul>
<li>You can now actually see ore in the rock on the screen, so you can see when it spawns</li>
@ -1170,8 +1130,7 @@ Thanks Andrew`,
{
category_id: 7,
title: 'Small tweaks to mining',
content:
`Today we've made the following small changes to mining:
content: `Today we've made the following small changes to mining:
<ul>
<li>You can now mine twice as much silver before getting tired. This is because it was pointed out that it shouldn't really by harder to get silver than gold. This actually now means silver is easier to get than it has ever been in the past</li>
@ -1188,8 +1147,7 @@ Remember we've added lots of new beds so you might not have to walk as far as yo
{
category_id: 1,
title: 'Fatigue reduced',
content:
`Due to popular demand, it is now possible to mine and fish for longer without getting tired!
content: `Due to popular demand, it is now possible to mine and fish for longer without getting tired!
<p>
There are now two sort of rocks you can mine:
<p>
@ -1205,8 +1163,7 @@ One final note: We are aware that the numbers of soft and hard rocks may need ba
{
category_id: 7,
title: 'Important note about RuneScape Windows client',
content:
`We've recently had a few queries from RuneScape users who say the RuneScape Windows client is crashing. (Even though it worked fine previously). We haven't changed our windows client recently, and from what we've been told it seems it's actually a recent windows update which is causing this problem.
content: `We've recently had a few queries from RuneScape users who say the RuneScape Windows client is crashing. (Even though it worked fine previously). We haven't changed our windows client recently, and from what we've been told it seems it's actually a recent windows update which is causing this problem.
<p>
We believe this problem has now been fixed in another windows update. Therefore if you are having problems with the RuneScape client failing we recommend you visit windowsupdate.com to get the latest patches. This should fix it.`,
date: '2002-11-28'
@ -1216,8 +1173,7 @@ We believe this problem has now been fixed in another windows update. Therefore
{
category_id: 2,
title: 'New website online!',
content:
`After many weeks of hard work our new website is finally online - think of it as an early Christmas Present! We hope that you will find the site easier to use and to find your way around.
content: `After many weeks of hard work our new website is finally online - think of it as an early Christmas Present! We hope that you will find the site easier to use and to find your way around.
<p>
Although we have tested the site as best we can, if you do find any problems, please let us know through the 'Feedback' section in our 'Customer Support' area accessible via the main page.
<p>
@ -1229,8 +1185,7 @@ Cheers,
{
category_id: 1,
title: 'Agility skill online',
content:
`We've just added another massive members update. The long awaited agility skill is finally available. You can practice on two different training courses, or try the agility dungeon.
content: `We've just added another massive members update. The long awaited agility skill is finally available. You can practice on two different training courses, or try the agility dungeon.
<p>
We've also added a new quest which uses the agility skill - called the "Grand tree quest". This is set in an enormous new area called the Gnome stronghold which also includes loads more besides the quest. Even more agility quests will of course be added in future updates.`,
date: '2002-12-12'
@ -1238,8 +1193,7 @@ We've also added a new quest which uses the agility skill - called the "Grand tr
{
category_id: 3,
title: 'Cheating scum banned',
content:
`If you cheat at RuneScape you will be banned. We have very advanced detection routines, and we can tell if you use programs to control your character for you. We've just banned over 400 players for cheating in this way. Many of whom were high level members.
content: `If you cheat at RuneScape you will be banned. We have very advanced detection routines, and we can tell if you use programs to control your character for you. We've just banned over 400 players for cheating in this way. Many of whom were high level members.
<p>
We'd like to remind our players that using macros to play for you is NOT allowed and we WILL permanantly ban you for it, no exceptions!`,
date: '2002-12-12'
@ -1249,21 +1203,21 @@ We'd like to remind our players that using macros to play for you is NOT allowed
{
category_id: 7,
title: 'San-Franciso servers online',
content: 'The RuneScape worlds were getting rather full again so we\'ve put some more online. As previously promised the servers are on the west coast of the US this time. We\'ve added 3 free servers, and 1 new members server',
content: "The RuneScape worlds were getting rather full again so we've put some more online. As previously promised the servers are on the west coast of the US this time. We've added 3 free servers, and 1 new members server",
date: '2002-12-23'
},
{
category_id: 1,
title: 'Merry Christmas',
content: 'Merry christmas from the Jagex team. We\'re dropping some santa hat\'s today at random intervals in random places. They\'re just a bit of fun though, so please don\'t be too upset if you can\'t find one. If you manage to get more than one then please consider giving them to your friends as christmas presents. Of course you can sell them but\'s not really the spirit of christmas :-)',
content:
"Merry christmas from the Jagex team. We're dropping some santa hat's today at random intervals in random places. They're just a bit of fun though, so please don't be too upset if you can't find one. If you manage to get more than one then please consider giving them to your friends as christmas presents. Of course you can sell them but's not really the spirit of christmas :-)",
date: '2002-12-25'
},
// after this RS starts keeping newsposts around
{
category_id: 5,
title: 'Siw Midtrud',
content:
`It was with great sadness that we learned of the death of Siw Midtrud, SIW39, a long time player of Runescape. Siw passed away last month after a long time fighting against disease.
content: `It was with great sadness that we learned of the death of Siw Midtrud, SIW39, a long time player of Runescape. Siw passed away last month after a long time fighting against disease.
<p>
Siw was an avid player of the game, currently ranked at 512 on the all-time list, with atttack of 81/81, strength of 91/91 and herblaw of 73/73.
<p>
@ -1273,8 +1227,7 @@ Siw was popular and well-loved by a great number of the Runescape community. Our
{
category_id: 2,
title: 'New News Section',
content:
`As you will no doubt have noticed, the Runescape news section has had a makeover. This is part of our effort to communicate to you more often, and we will be regularly posting news in the appropriate sections that you can see above.
content: `As you will no doubt have noticed, the Runescape news section has had a makeover. This is part of our effort to communicate to you more often, and we will be regularly posting news in the appropriate sections that you can see above.
<p>
Clicking any of the category names will show only news of that type, most recent first. Clicking &#039;All Categories&#039; will show you all news items.
<p>
@ -1284,8 +1237,7 @@ We hope that you will find the new news section easy to use, and more importantl
{
category_id: 3,
title: 'Credit card fraud warning',
content:
`Don&#039;t accept membership from other players - doing so is dangerous!
content: `Don&#039;t accept membership from other players - doing so is dangerous!
<p>
We would like to remind everyone that you must <b>NOT</b> allow another player to pay for your runescape account membership.
<p>
@ -1299,8 +1251,7 @@ Fraudulent payments will be unearthed and result in automatic and permanent bans
{
category_id: 5,
title: 'Wedding bells at Jagex!',
content:
`<p align= justify>
content: `<p align= justify>
Simon Brace, Jagex&#039;s most eligible batchelor is no longer eligible. That&#039;s right, someone&#039;s actually agreed to marry him! Runescape players will know our husband to be
as &quot;modsimon&quot;. The scourge of hackers everywhere, Simon
frequently appears on-line, valiantly fighting customer
@ -1337,8 +1288,7 @@ Simon Brace, Jagex&#039;s most eligible batchelor is no longer eligible. Th
{
category_id: 2,
title: 'Last chance to move items',
content:
`We are planning on removing the RS-Classic to RuneScape item-transferal page tomorrow (on Friday 23rd-Apr) As we&#039;ve said previously we don&#039;t want to keep the two games permanently linked, because doing so limits the potential of our new game engine.
content: `We are planning on removing the RS-Classic to RuneScape item-transferal page tomorrow (on Friday 23rd-Apr) As we&#039;ve said previously we don&#039;t want to keep the two games permanently linked, because doing so limits the potential of our new game engine.
<p>
We are instead going to run them as two separate games, which don&#039;t affect each other. In particular we want to increase the amount of bank space RuneScape players have, but can&#039;t do this whilst the bank system is still attached to the old RS-Classic bank system.
<p>
@ -1351,16 +1301,14 @@ The Jagex Team`,
{
category_id: 2,
title: 'Guthix returns...',
content:
`...to speak his mind once more in <a href=/varrock/letters/19 class=c>Letters 19</a>.<p>
content: `...to speak his mind once more in <a href=/varrock/letters/19 class=c>Letters 19</a>.<p>
Fans of the deity who can&#039;t make up his mind as to which side he&#039;s on, should make their way to the <a href=/varrock class=c>Library of Varrock</a> and see what secrets the RuneScape gods have revealed in this edition.`,
date: '2004-04-23'
},
{
category_id: 1,
title: 'Bigger Banks & Treasure Trails',
content:
`<b>Bigger bank space</b><br>
content: `<b>Bigger bank space</b><br>
As promised we have now increased the size of your bank accounts. We have increased the size of both free and members banks by 25%. So free users get another 12 slots and members get another 48.
<p>
<b>Treasure trails</b><br>
@ -1383,8 +1331,7 @@ Various players have pointed out to us the spawn rate of certain herblaw ingredi
{
category_id: 1,
title: 'Dueling changes',
content:
`Rangers now get arrows back from duels. Previously when a duel ended a ranger would be teleported out, leaving all the arrows they had fired on the ground.<br> Now they get the arrows put back in their inventory at the end instead.
content: `Rangers now get arrows back from duels. Previously when a duel ended a ranger would be teleported out, leaving all the arrows they had fired on the ground.<br> Now they get the arrows put back in their inventory at the end instead.
<p>
<b>New duel options</b>
<p>
@ -1398,8 +1345,7 @@ People were commenting that the fire giants under the waterfall were getting ove
{
category_id: 4,
title: 'World 17 under repair',
content:
`The server which runs world 17 in Toronto seems to have suffered a hardware failure.
content: `The server which runs world 17 in Toronto seems to have suffered a hardware failure.
<p>
We are currently looking into getting the machine fixed, but the world may be offline for a few days if we have to ship it back to the manufacturer for repairs.
<p>
@ -1409,8 +1355,7 @@ Please use a different world in the meantime. Thanks for your patience, we&#039;
{
category_id: 1,
title: 'Big Chompy Bird Hunting',
content:
`City life doesn&#039;t suit everyone, which is why Rantz and the rest of his ogre family have set up residence on the east coast in a cosy little cave with a nice view of the sea.<br>
content: `City life doesn&#039;t suit everyone, which is why Rantz and the rest of his ogre family have set up residence on the east coast in a cosy little cave with a nice view of the sea.<br>
However, independence comes at a cost. There&#039;s no more easy meals for the family and they currently have a hankering for Chompy Bird, a favourite delicacy of ogres.
<p>
Rantz is keen to start the Chompy Bird hunting season, but can&#039;t get started. He&#039;s all fingers and thumbs when it comes to making ogre arrows for his huge ogre bow.<br>
@ -1421,8 +1366,7 @@ Find out if you have what it takes in our new <a href=/rs2/quests/chompy class=c
{
category_id: 3,
title: 'UK SMS Telebilling Change',
content:
`We have recently upgraded our UK PayBySMS Telebilling service.
content: `We have recently upgraded our UK PayBySMS Telebilling service.
<p>
Please note that when sending an SMS to subscribe using PayBySMS in the UK, you should now send to the new shortcode <b>89118</b>.
<p>
@ -1434,8 +1378,7 @@ RuneScape Billing Support`,
{
category_id: 2,
title: 'Hiscores Improved',
content:
`Players will be glad to hear that today we have expanded our RuneScape hiscores list to show the <b>top 500,000</b> players in every skill.<p>
content: `Players will be glad to hear that today we have expanded our RuneScape hiscores list to show the <b>top 500,000</b> players in every skill.<p>
We have made this change as the competition was getting extremely fierce for a place in the hiscores in certain stats. You now have a greater chance of being able to see how you rank against your friends and enemies in your favourite skill!
<p>
Please note that you still need at least level 30 in a skill to appear in the relevant table. Also players who have not yet played since the change will not appear in the table until they login, so it will take a few days to fill up.`,
@ -1444,13 +1387,13 @@ Please note that you still need at least level 30 in a skill to appear in the re
{
category_id: 2,
title: 'World Map Updated',
content:
`We are pleased to announce that we have once again updated our World Map of the RuneScape world!
content: `We are pleased to announce that we have once again updated our World Map of the RuneScape world!
<p>
You can see the new version of the World Map, complete with all the latest map areas in the &#039;How To Play&#039; section of our front page, or by clicking <a href=worldmap class=c>here</a>.`,
date: '2004-05-25'
}
])
.ignore().execute();
.ignore()
.execute();
process.exit(0);

View file

@ -1,8 +1,6 @@
import type { ColumnType } from 'kysely';
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
: ColumnType<T, T | undefined, T>;
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>;
export type Timestamp = ColumnType<Date, Date | string, Date | string>;
export type account = {

View file

@ -22,7 +22,10 @@ export default class GameMap {
console.time('Loading game map');
const maps = fs.readdirSync('data/pack/server/maps').filter(x => x[0] === 'm');
for (let index = 0; index < maps.length; index++) {
const [fileX, fileZ] = maps[index].substring(1).split('_').map(x => parseInt(x));
const [fileX, fileZ] = maps[index]
.substring(1)
.split('_')
.map(x => parseInt(x));
const mapsquareX = fileX << 6;
const mapsquareZ = fileZ << 6;
@ -30,8 +33,8 @@ export default class GameMap {
while (npcMap.available > 0) {
const pos = npcMap.g2();
const level = (pos >> 12) & 0x3;
const localX = (pos >> 6) & 0x3F;
const localZ = (pos & 0x3F);
const localX = (pos >> 6) & 0x3f;
const localZ = pos & 0x3f;
const count = npcMap.g1();
for (let j = 0; j < count; j++) {
@ -40,17 +43,7 @@ export default class GameMap {
const size = npcType.size;
const npc = new Npc(
level,
mapsquareX + localX,
mapsquareZ + localZ,
size,
size,
World.getNextNid(),
id,
npcType.moverestrict,
npcType.blockwalk
);
const npc = new Npc(level, mapsquareX + localX, mapsquareZ + localZ, size, size, World.getNextNid(), id, npcType.moverestrict, npcType.blockwalk);
if (npcType.members === true && World.members === true) {
World.addNpc(npc);
@ -64,21 +57,15 @@ export default class GameMap {
while (objMap.available > 0) {
const pos = objMap.g2();
const level = (pos >> 12) & 0x3;
const localX = (pos >> 6) & 0x3F;
const localZ = (pos & 0x3F);
const localX = (pos >> 6) & 0x3f;
const localZ = pos & 0x3f;
const count = objMap.g1();
for (let j = 0; j < count; j++) {
const objId = objMap.g2();
const objCount = objMap.g1();
const obj = new Obj(
level,
mapsquareX + localX,
mapsquareZ + localZ,
objId,
objCount
);
const obj = new Obj(level, mapsquareX + localX, mapsquareZ + localZ, objId, objCount);
this.zoneManager.getZone(obj.x, obj.z, obj.level).addStaticObj(obj);
}

View file

@ -1,8 +1,8 @@
import InvType from '#lostcity/cache/InvType.js';
import ObjType from '#lostcity/cache/ObjType.js';
type Item = { id: number, count: number }
type TransactionResult = { slot: number, item: Item }
type Item = { id: number; count: number };
type TransactionResult = { slot: number; item: Item };
export class InventoryTransaction {
requested = 0;
@ -35,9 +35,8 @@ export class InventoryTransaction {
}
}
export class Inventory {
static STACK_LIMIT = 0x7FFFFFFF/* - 1*/;
static STACK_LIMIT = 0x7fffffff /* - 1*/;
static NORMAL_STACK = 0;
static ALWAYS_STACK = 1;

View file

@ -104,7 +104,8 @@ class Login {
player.webClient = client.isWebSocket();
World.addPlayer(player, client);
} break;
break;
}
case 'logoutreply': {
const { username } = msg;
@ -120,7 +121,8 @@ class Login {
this.logoutRequests.delete(player.username37);
}
} break;
break;
}
default:
throw new Error('Unknown message type: ' + msg.type);
}

View file

@ -1,8 +1,8 @@
import {Worker} from 'worker_threads';
import { Worker } from 'worker_threads';
import Packet from '#jagex2/io/Packet.js';
import {toBase37} from '#jagex2/jstring/JString.js';
import { toBase37 } from '#jagex2/jstring/JString.js';
import CategoryType from '#lostcity/cache/CategoryType.js';
import DbRowType from '#lostcity/cache/DbRowType.js';
@ -27,7 +27,7 @@ import VarSharedType from '#lostcity/cache/VarSharedType.js';
import WordEnc from '#lostcity/cache/WordEnc.js';
import GameMap from '#lostcity/engine/GameMap.js';
import {Inventory} from '#lostcity/engine/Inventory.js';
import { Inventory } from '#lostcity/engine/Inventory.js';
import Login from '#lostcity/engine/Login.js';
import CollisionManager from '#lostcity/engine/collision/CollisionManager.js';
@ -44,14 +44,14 @@ import Npc from '#lostcity/entity/Npc.js';
import Obj from '#lostcity/entity/Obj.js';
import Player from '#lostcity/entity/Player.js';
import {ClientProtLengths} from '#lostcity/server/ClientProt.js';
import { ClientProtLengths } from '#lostcity/server/ClientProt.js';
import ClientSocket from '#lostcity/server/ClientSocket.js';
import {ServerProt} from '#lostcity/server/ServerProt.js';
import { ServerProt } from '#lostcity/server/ServerProt.js';
import Environment from '#lostcity/util/Environment.js';
import {CollisionFlagMap, LineValidator, NaivePathFinder, PathFinder, StepValidator} from '@2004scape/rsmod-pathfinder';
import {PlayerQueueType} from '#lostcity/entity/EntityQueueRequest.js';
import {PlayerTimerType} from '#lostcity/entity/EntityTimer.js';
import { CollisionFlagMap, LineValidator, NaivePathFinder, PathFinder, StepValidator } from '@2004scape/rsmod-pathfinder';
import { PlayerQueueType } from '#lostcity/entity/EntityQueueRequest.js';
import { PlayerTimerType } from '#lostcity/entity/EntityTimer.js';
class World {
id = Environment.WORLD_ID as number;
@ -615,7 +615,7 @@ class World {
this.players[index] = player;
this.playerIds[pid] = index;
player.pid = pid;
player.uid = ((Number(player.username37 & 0x1FFFFFn) << 11) | player.pid) >>> 0;
player.uid = ((Number(player.username37 & 0x1fffffn) << 11) | player.pid) >>> 0;
this.getZone(player.x, player.z, player.level).enter(player);
@ -781,7 +781,6 @@ class World {
inv.update = false;
// Increase or Decrease shop stock
const invType = InvType.get(inv.type);
@ -1177,7 +1176,7 @@ class World {
let opcode = stream.g1();
if (socket.decryptor) {
opcode = (opcode - socket.decryptor.nextInt()) & 0xFF;
opcode = (opcode - socket.decryptor.nextInt()) & 0xff;
stream.data[start] = opcode;
}
@ -1251,20 +1250,20 @@ class World {
}
getPlayerByUid(uid: number) {
const pid = uid & 0x7FF;
const name37 = (uid >> 11) & 0x1FFFFF;
const pid = uid & 0x7ff;
const name37 = (uid >> 11) & 0x1fffff;
const slot = this.playerIds[pid];
if (slot === -1) {
return null;
}
const player = this.players[slot];
if (!player) {
return null;
}
if (Number(player.username37 & 0x1FFFFFn) !== name37) {
if (Number(player.username37 & 0x1fffffn) !== name37) {
return null;
}
@ -1289,8 +1288,8 @@ class World {
}
getNpcByUid(uid: number) {
const slot = uid & 0xFFFF;
const type = (uid >> 16) & 0xFFFF;
const slot = uid & 0xffff;
const type = (uid >> 16) & 0xffff;
const npc = this.npcs[slot];
if (!npc || npc.type !== type) {

View file

@ -23,7 +23,7 @@ if (fs.existsSync('data/config/worlds.json')) {
if (Environment.LOCAL_DEV) {
let address = (Environment.HTTPS_ENABLED ? 'https://' : 'http://') + Environment.PUBLIC_IP;
if (Environment.ADDRESS_SHOWPORT) {
if (Environment.HTTPS_ENABLED && (Environment.WEB_PORT != 443 && Environment.WEB_PORT != 80)) {
if (Environment.HTTPS_ENABLED && Environment.WEB_PORT != 443 && Environment.WEB_PORT != 80) {
address += ':' + Environment.WEB_PORT;
} else if (!Environment.HTTPS_ENABLED && Environment.WEB_PORT != 80) {
address += ':' + Environment.WEB_PORT;

View file

@ -61,10 +61,13 @@ export default class CollisionManager {
const maps = fs.readdirSync('data/pack/server/maps').filter(x => x[0] === 'm');
for (let index = 0; index < maps.length; index++) {
const [fileX, fileZ] = maps[index].substring(1).split('_').map(x => parseInt(x));
const [fileX, fileZ] = maps[index]
.substring(1)
.split('_')
.map(x => parseInt(x));
const mapsquareX = fileX << 6;
const mapsquareZ = fileZ << 6;
const mapsquareId = fileX << 8 | fileZ;
const mapsquareId = (fileX << 8) | fileZ;
const landData = Packet.load(`data/pack/server/maps/m${fileX}_${fileZ}`);
lands.set(mapsquareId, new Array<number>(4 * 64 * 64)); // 4 * 64 * 64 size is guaranteed for lands
@ -134,16 +137,7 @@ export default class CollisionManager {
const width = type.width;
const length = type.length;
const loc = new Loc(
adjustedLevel,
absoluteX,
absoluteZ,
width,
length,
locId,
shape,
angle
);
const loc = new Loc(adjustedLevel, absoluteX, absoluteZ, width, length, locId, shape, angle);
zoneManager.getZone(absoluteX, absoluteZ, adjustedLevel).addStaticLoc(loc);
@ -162,12 +156,7 @@ export default class CollisionManager {
* @param level The level pos.
* @param add True if adding this collision. False if removing.
*/
changeLandCollision(
x: number,
z: number,
level: number,
add: boolean
): void {
changeLandCollision(x: number, z: number, level: number, add: boolean): void {
this.floorCollider.change(x, z, level, add);
}
@ -184,18 +173,7 @@ export default class CollisionManager {
* @param level The level pos.
* @param add True if adding this collision. False if removing.
*/
changeLocCollision(
shape: number,
angle: number,
blockrange: boolean,
length: number,
width: number,
active: number,
x: number,
z: number,
level: number,
add: boolean,
): void {
changeLocCollision(shape: number, angle: number, blockrange: boolean, length: number, width: number, active: number, x: number, z: number, level: number, add: boolean): void {
switch (LocShapes.layer(shape)) {
case LocLayer.WALL:
this.wallCollider.change(x, z, level, angle, shape, blockrange, add);
@ -227,13 +205,7 @@ export default class CollisionManager {
* @param level The level pos.
* @param add True if adding this collision. False if removing.
*/
changeNpcCollision(
size: number,
x: number,
z: number,
level: number,
add: boolean
): void {
changeNpcCollision(size: number, x: number, z: number, level: number, add: boolean): void {
this.npcCollider.change(x, z, level, size, add);
}
@ -245,13 +217,7 @@ export default class CollisionManager {
* @param level The level pos.
* @param add True if adding this collision. False if removing.
*/
changePlayerCollision(
size: number,
x: number,
z: number,
level: number,
add: boolean
): void {
changePlayerCollision(size: number, x: number, z: number, level: number, add: boolean): void {
this.playerCollider.change(x, z, level, size, add);
}
@ -262,12 +228,7 @@ export default class CollisionManager {
* @param level The level pos.
* @param add True if adding this collision. False if removing.
*/
changeRoofCollision(
x: number,
z: number,
level: number,
add: boolean
): void {
changeRoofCollision(x: number, z: number, level: number, add: boolean): void {
this.roofCollider.change(x, z, level, add);
}
@ -321,37 +282,28 @@ export default class CollisionManager {
}
}
private packCoord(
x: number,
z: number,
level: number
): number {
return ((z & 0x3F) | ((x & 0x3F) << 6) | ((level & 0x3) << 12));
private packCoord(x: number, z: number, level: number): number {
return (z & 0x3f) | ((x & 0x3f) << 6) | ((level & 0x3) << 12);
}
private unpackCoord(packed: number) {
const z = packed & 0x3F;
const x = (packed >> 6) & 0x3F;
const z = packed & 0x3f;
const x = (packed >> 6) & 0x3f;
const level = (packed >> 12) & 0x3;
return { x, z, level };
}
private packLoc(
id: number,
shape: number,
angle: number,
coord: number
): number {
const lowBits = (id & 0xFFFF) | ((shape & 0x1F) << 16) | ((angle & 0x3) << 21);
const highBits = (coord & 0x3FFF);
return lowBits + (highBits * CollisionManager.SHIFT_23);
private packLoc(id: number, shape: number, angle: number, coord: number): number {
const lowBits = (id & 0xffff) | ((shape & 0x1f) << 16) | ((angle & 0x3) << 21);
const highBits = coord & 0x3fff;
return lowBits + highBits * CollisionManager.SHIFT_23;
}
private unpackLoc(packed: number) {
const id = packed & 0xFFFF;
const shape = (packed >> 16) & 0x1F;
const id = packed & 0xffff;
const shape = (packed >> 16) & 0x1f;
const angle = (packed >> 21) & 0x3;
const coord = (packed / CollisionManager.SHIFT_23) & 0x3FFF;
const coord = (packed / CollisionManager.SHIFT_23) & 0x3fff;
return { id, shape, angle, coord };
}
}

View file

@ -7,12 +7,7 @@ export default class FloorCollider {
this.flags = flags;
}
change(
x: number,
z: number,
level: number,
add: boolean
): void {
change(x: number, z: number, level: number, add: boolean): void {
if (add) {
this.flags.add(x, z, level, CollisionFlag.FLOOR);
} else {

View file

@ -7,22 +7,14 @@ export default class LocCollider {
this.flags = flags;
}
change(
x: number,
z: number,
level: number,
width: number,
length: number,
blockrange: boolean,
add: boolean
): void {
change(x: number, z: number, level: number, width: number, length: number, blockrange: boolean, add: boolean): void {
let mask = CollisionFlag.LOC;
if (blockrange) {
mask |= CollisionFlag.LOC_PROJ_BLOCKER;
}
for (let index = 0; index < width * length; index++) {
const deltaX = x + (index % width);
const deltaZ = z + (index / width);
const deltaZ = z + index / width;
if (add) {
this.flags.add(deltaX, deltaZ, level, mask);
} else {

View file

@ -7,17 +7,11 @@ export default class NpcCollider {
this.flags = flags;
}
change(
x: number,
z: number,
level: number,
size: number,
add: boolean
): void {
change(x: number, z: number, level: number, size: number, add: boolean): void {
const mask = CollisionFlag.NPC;
for (let index = 0; index < size * size; index++) {
const deltaX = x + (index % size);
const deltaZ = z + (index / size);
const deltaZ = z + index / size;
if (add) {
this.flags.add(deltaX, deltaZ, level, mask);
} else {

View file

@ -7,17 +7,11 @@ export default class PlayerCollider {
this.flags = flags;
}
change(
x: number,
z: number,
level: number,
size: number,
add: boolean
): void {
change(x: number, z: number, level: number, size: number, add: boolean): void {
const mask = CollisionFlag.PLAYER;
for (let index = 0; index < size * size; index++) {
const deltaX = x + (index % size);
const deltaZ = z + (index / size);
const deltaZ = z + index / size;
if (add) {
this.flags.add(deltaX, deltaZ, level, mask);
} else {

View file

@ -7,12 +7,7 @@ export default class RoofCollider {
this.flags = flags;
}
change(
x: number,
z: number,
level: number,
add: boolean
): void {
change(x: number, z: number, level: number, add: boolean): void {
if (add) {
this.flags.add(x, z, level, CollisionFlag.ROOF);
} else {

View file

@ -16,15 +16,7 @@ export default class WallCollider {
this.wallCornerLCollider = new WallCornerLCollider(flags);
}
change(
x: number,
z: number,
level: number,
angle: number,
shape: number,
blockrange: boolean,
add: boolean
): void {
change(x: number, z: number, level: number, angle: number, shape: number, blockrange: boolean, add: boolean): void {
switch (shape) {
case LocShape.WALL_STRAIGHT:
this.wallStraightCollider.change(x, z, level, angle, blockrange, add);

View file

@ -9,14 +9,7 @@ export default class WallCornerCollider {
this.flags = flags;
}
change(
x: number,
z: number,
level: number,
angle: number,
blockrange: boolean,
add: boolean
): void {
change(x: number, z: number, level: number, angle: number, blockrange: boolean, add: boolean): void {
const northWest = blockrange ? CollisionFlag.WALL_NORTH_WEST_PROJ_BLOCKER : CollisionFlag.WALL_NORTH_WEST;
const southEast = blockrange ? CollisionFlag.WALL_SOUTH_EAST_PROJ_BLOCKER : CollisionFlag.WALL_SOUTH_EAST;
const northEast = blockrange ? CollisionFlag.WALL_NORTH_EAST_PROJ_BLOCKER : CollisionFlag.WALL_NORTH_EAST;

View file

@ -9,14 +9,7 @@ export default class WallCornerLCollider {
this.flags = flags;
}
change(
x: number,
z: number,
level: number,
angle: number,
blockrange: boolean,
add: boolean
): void {
change(x: number, z: number, level: number, angle: number, blockrange: boolean, add: boolean): void {
const west = blockrange ? CollisionFlag.WALL_WEST_PROJ_BLOCKER : CollisionFlag.WALL_WEST;
const east = blockrange ? CollisionFlag.WALL_EAST_PROJ_BLOCKER : CollisionFlag.WALL_EAST;
const north = blockrange ? CollisionFlag.WALL_NORTH_PROJ_BLOCKER : CollisionFlag.WALL_NORTH;

View file

@ -9,14 +9,7 @@ export default class WallStraightCollider {
this.flags = flags;
}
change(
x: number,
z: number,
level: number,
angle: number,
blockrange: boolean,
add: boolean
): void {
change(x: number, z: number, level: number, angle: number, blockrange: boolean, add: boolean): void {
const west = blockrange ? CollisionFlag.WALL_WEST_PROJ_BLOCKER : CollisionFlag.WALL_WEST;
const east = blockrange ? CollisionFlag.WALL_EAST_PROJ_BLOCKER : CollisionFlag.WALL_EAST;
const north = blockrange ? CollisionFlag.WALL_NORTH_PROJ_BLOCKER : CollisionFlag.WALL_NORTH;

View file

@ -5,17 +5,17 @@ import Packet from '#jagex2/io/Packet.js';
import ScriptOpcode from '#lostcity/engine/script/ScriptOpcode.js';
export interface ScriptInfo {
scriptName: string,
sourceFilePath: string,
lookupKey: number,
parameterTypes: number[],
pcs: number[],
lines: number[]
scriptName: string;
sourceFilePath: string;
lookupKey: number;
parameterTypes: number[];
pcs: number[];
lines: number[];
}
export type SwitchTable = {
[key: number]: number | undefined
}
[key: number]: number | undefined;
};
// compiled bytecode representation
export default class Script {

View file

@ -383,7 +383,7 @@ enum ScriptOpcode {
// Debug ops (10000-11000)
ERROR = 10000,
MAP_LOCALDEV,
MAP_LOCALDEV
}
export default ScriptOpcode;

View file

@ -2,12 +2,12 @@ import ScriptOpcode from '#lostcity/engine/script/ScriptOpcode.js';
const ScriptOpcodePointers: {
[key: string]: {
require?: string[],
corrupt?: string[],
set?: string[],
conditional?: boolean,
secondary?: boolean,
secondaryRequire?: string[]
require?: string[];
corrupt?: string[];
set?: string[];
conditional?: boolean;
secondary?: boolean;
secondaryRequire?: string[];
};
} = {
// Core language ops
@ -159,7 +159,7 @@ const ScriptOpcodePointers: {
require: ['active_player']
},
[ScriptOpcode.HUNTALL]: {
set: ['find_player'],
set: ['find_player']
},
[ScriptOpcode.HUNTNEXT]: {
require: ['find_player'],
@ -295,8 +295,18 @@ const ScriptOpcodePointers: {
require: ['p_active_player'],
corrupt: [
// everything except active is assumed corrupted
'find_player', 'find_npc', 'find_loc', 'find_obj', 'find_db',
'last_com', 'last_int', 'last_item', 'last_slot', 'last_targetslot', 'last_useitem', 'last_useslot'
'find_player',
'find_npc',
'find_loc',
'find_obj',
'find_db',
'last_com',
'last_int',
'last_item',
'last_slot',
'last_targetslot',
'last_useitem',
'last_useslot'
]
},
[ScriptOpcode.P_COUNTDIALOG]: {
@ -304,16 +314,35 @@ const ScriptOpcodePointers: {
set: ['last_int'],
corrupt: [
// everything except active is assumed corrupted
'find_player', 'find_npc', 'find_loc', 'find_obj', 'find_db',
'last_com', 'last_item', 'last_slot', 'last_targetslot', 'last_useitem', 'last_useslot'
'find_player',
'find_npc',
'find_loc',
'find_obj',
'find_db',
'last_com',
'last_item',
'last_slot',
'last_targetslot',
'last_useitem',
'last_useslot'
]
},
[ScriptOpcode.P_DELAY]: {
require: ['p_active_player'],
corrupt: [
// everything except active is assumed corrupted
'find_player', 'find_npc', 'find_loc', 'find_obj', 'find_db',
'last_com', 'last_int', 'last_item', 'last_slot', 'last_targetslot', 'last_useitem', 'last_useslot'
'find_player',
'find_npc',
'find_loc',
'find_obj',
'find_db',
'last_com',
'last_int',
'last_item',
'last_slot',
'last_targetslot',
'last_useitem',
'last_useslot'
]
},
[ScriptOpcode.P_EXACTMOVE]: {
@ -351,8 +380,18 @@ const ScriptOpcodePointers: {
require: ['p_active_player'],
corrupt: [
// everything except active is assumed corrupted
'find_player', 'find_npc', 'find_loc', 'find_obj', 'find_db',
'last_com', 'last_int', 'last_item', 'last_slot', 'last_targetslot', 'last_useitem', 'last_useslot'
'find_player',
'find_npc',
'find_loc',
'find_obj',
'find_db',
'last_com',
'last_int',
'last_item',
'last_slot',
'last_targetslot',
'last_useitem',
'last_useslot'
]
},
[ScriptOpcode.P_STOPACTION]: {
@ -493,8 +532,18 @@ const ScriptOpcodePointers: {
require: ['active_npc'],
corrupt: [
// everything except active is assumed corrupted
'find_player', 'find_npc', 'find_loc', 'find_obj', 'find_db',
'last_com', 'last_int', 'last_item', 'last_slot', 'last_targetslot', 'last_useitem', 'last_useslot'
'find_player',
'find_npc',
'find_loc',
'find_obj',
'find_db',
'last_com',
'last_int',
'last_item',
'last_slot',
'last_targetslot',
'last_useitem',
'last_useslot'
],
secondary: true,
secondaryRequire: ['active_npc2']
@ -889,7 +938,7 @@ const ScriptOpcodePointers: {
},
[ScriptOpcode.DB_FIND_REFINE]: {
require: ['find_db']
},
}
};
export default ScriptOpcodePointers;

View file

@ -42,7 +42,7 @@ export default class ScriptProvider {
const version = dat.g4();
if (version !== ScriptProvider.COMPILER_VERSION) {
throw new Error(`Compiler is out of date - have version ${version} but we're expecting ${(ScriptProvider.COMPILER_VERSION)}. Check the #dev-resources channel in Discord for the latest RuneScriptCompiler.jar.`);
throw new Error(`Compiler is out of date - have version ${version} but we're expecting ${ScriptProvider.COMPILER_VERSION}. Check the #dev-resources channel in Discord for the latest RuneScriptCompiler.jar.`);
}
ScriptProvider.scripts = [];
@ -62,7 +62,7 @@ export default class ScriptProvider {
ScriptProvider.scriptNames.set(script.name, id);
// add the script to lookup table if the value isn't -1
if (script.info.lookupKey !== 0xFFFFFFFF) {
if (script.info.lookupKey !== 0xffffffff) {
ScriptProvider.scriptLookup.set(script.info.lookupKey, script);
}
@ -108,11 +108,11 @@ export default class ScriptProvider {
* @param category The script subject category id.
*/
static getByTrigger(trigger: ServerTriggerType | NpcMode, type: number = -1, category: number = -1): Script | undefined {
let script = ScriptProvider.scriptLookup.get(trigger | 0x2 << 8 | type << 10);
let script = ScriptProvider.scriptLookup.get(trigger | (0x2 << 8) | (type << 10));
if (script) {
return script;
}
script = ScriptProvider.scriptLookup.get(trigger | 0x1 << 8 | category << 10);
script = ScriptProvider.scriptLookup.get(trigger | (0x1 << 8) | (category << 10));
if (script) {
return script;
}
@ -132,9 +132,9 @@ export default class ScriptProvider {
*/
static getByTriggerSpecific(trigger: ServerTriggerType, type: number = -1, category: number = -1): Script | undefined {
if (type !== -1) {
return ScriptProvider.scriptLookup.get(trigger | 0x2 << 8 | type << 10);
return ScriptProvider.scriptLookup.get(trigger | (0x2 << 8) | (type << 10));
} else if (category !== -1) {
return ScriptProvider.scriptLookup.get(trigger | 0x1 << 8 | category << 10);
return ScriptProvider.scriptLookup.get(trigger | (0x1 << 8) | (category << 10));
}
return ScriptProvider.scriptLookup.get(trigger);
}

View file

@ -30,8 +30,8 @@ import Player from '#lostcity/entity/Player.js';
export type CommandHandler = (state: ScriptState) => void;
export type CommandHandlers = {
[opcode: number]: CommandHandler
}
[opcode: number]: CommandHandler;
};
// script executor
export default class ScriptRunner {
@ -51,7 +51,7 @@ export default class ScriptRunner {
...StringOps,
...NumberOps,
...DbOps,
...DebugOps,
...DebugOps
};
/**
@ -151,10 +151,8 @@ export default class ScriptRunner {
const opcode = state.script.opcodes[state.pc];
let secondary = state.intOperand;
if (opcode === ScriptOpcode.POP_VARP || opcode === ScriptOpcode.POP_VARN ||
opcode === ScriptOpcode.PUSH_VARP || opcode === ScriptOpcode.PUSH_VARN)
{
secondary = state.intOperand >> 16 & 0x1;
if (opcode === ScriptOpcode.POP_VARP || opcode === ScriptOpcode.POP_VARN || opcode === ScriptOpcode.PUSH_VARP || opcode === ScriptOpcode.PUSH_VARN) {
secondary = (state.intOperand >> 16) & 0x1;
} else if (opcode <= ScriptOpcode.POP_ARRAY_INT) {
secondary = 0;
}

View file

@ -14,16 +14,16 @@ import Player from '#lostcity/entity/Player.js';
import { toInt32 } from '#lostcity/util/Numbers.js';
export interface GosubStackFrame {
script: Script,
pc: number,
intLocals: number[],
stringLocals: string[],
script: Script;
pc: number;
intLocals: number[];
stringLocals: string[];
}
// for debugging stack traces
export interface JumpStackFrame {
script: Script,
pc: number
script: Script;
pc: number;
}
export default class ScriptState {
@ -122,7 +122,7 @@ export default class ScriptState {
constructor(script: Script, args: ScriptArgument[] | null = []) {
this.script = script;
this.trigger = script.info.lookupKey & 0xFF;
this.trigger = script.info.lookupKey & 0xff;
if (args) {
for (let i = 0; i < args.length; i++) {

View file

@ -154,7 +154,7 @@ enum ServerTriggerType {
MOVECHECK = 159,
AI_MOVECHECK = 160,
LEVELUP = 161,
LEVELUP = 161
}
namespace ServerTriggerType {

View file

@ -21,7 +21,7 @@ function gosub(state: ScriptState, id: number) {
script: state.script,
pc: state.pc,
intLocals: state.intLocals,
stringLocals: state.stringLocals,
stringLocals: state.stringLocals
};
// lookup script and set it up
@ -56,22 +56,22 @@ function setupNewScript(state: ScriptState, script: Script) {
}
const CoreOps: CommandHandlers = {
[ScriptOpcode.PUSH_CONSTANT_INT]: (state) => {
[ScriptOpcode.PUSH_CONSTANT_INT]: state => {
state.pushInt(state.intOperand);
},
[ScriptOpcode.PUSH_CONSTANT_STRING]: (state) => {
[ScriptOpcode.PUSH_CONSTANT_STRING]: state => {
state.pushString(state.stringOperand);
},
[ScriptOpcode.PUSH_VARP]: (state) => {
const secondary = state.intOperand >> 16 & 0x1;
[ScriptOpcode.PUSH_VARP]: state => {
const secondary = (state.intOperand >> 16) & 0x1;
if (secondary && !state._activePlayer2) {
throw new Error('No secondary active_player.');
} else if (!secondary && !state._activePlayer) {
throw new Error('No active_player.');
}
const varp = state.intOperand & 0xFFFF;
const varp = state.intOperand & 0xffff;
if (!secondary) {
state.pushInt(state._activePlayer!.getVarp(varp));
} else {
@ -79,15 +79,15 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.POP_VARP]: (state) => {
const secondary = state.intOperand >> 16 & 0x1;
[ScriptOpcode.POP_VARP]: state => {
const secondary = (state.intOperand >> 16) & 0x1;
if (secondary && !state._activePlayer2) {
throw new Error('No secondary active_player.');
} else if (!secondary && !state._activePlayer) {
throw new Error('No active_player.');
}
const varp = state.intOperand & 0xFFFF;
const varp = state.intOperand & 0xffff;
const type = VarPlayerType.get(varp);
if (!state.pointerGet(ProtectedActivePlayer[secondary]) && type.protect) {
throw new Error(`%${type.debugname} requires protected access`);
@ -101,14 +101,14 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.PUSH_VARN]: (state) => {
const secondary = state.intOperand >> 16 & 0x1;
[ScriptOpcode.PUSH_VARN]: state => {
const secondary = (state.intOperand >> 16) & 0x1;
if (secondary && !state._activeNpc2) {
throw new Error('No secondary active_npc.');
} else if (!secondary && !state._activeNpc) {
throw new Error('No active_npc.');
}
const varn = state.intOperand & 0xFFFF;
const varn = state.intOperand & 0xffff;
if (!secondary) {
state.pushInt(state._activeNpc!.getVar(varn));
} else {
@ -116,14 +116,14 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.POP_VARN]: (state) => {
const secondary = state.intOperand >> 16 & 0x1;
[ScriptOpcode.POP_VARN]: state => {
const secondary = (state.intOperand >> 16) & 0x1;
if (secondary && !state._activeNpc2) {
throw new Error('No secondary active_npc.');
} else if (!secondary && !state._activeNpc) {
throw new Error('No active_npc.');
}
const varn = state.intOperand & 0xFFFF;
const varn = state.intOperand & 0xffff;
const value = state.popInt();
if (!secondary) {
state._activeNpc!.setVar(varn, value);
@ -132,27 +132,27 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.PUSH_INT_LOCAL]: (state) => {
[ScriptOpcode.PUSH_INT_LOCAL]: state => {
state.pushInt(state.intLocals[state.intOperand]);
},
[ScriptOpcode.POP_INT_LOCAL]: (state) => {
[ScriptOpcode.POP_INT_LOCAL]: state => {
state.intLocals[state.intOperand] = state.popInt();
},
[ScriptOpcode.PUSH_STRING_LOCAL]: (state) => {
[ScriptOpcode.PUSH_STRING_LOCAL]: state => {
state.pushString(state.stringLocals[state.intOperand]);
},
[ScriptOpcode.POP_STRING_LOCAL]: (state) => {
[ScriptOpcode.POP_STRING_LOCAL]: state => {
state.stringLocals[state.intOperand] = state.popString();
},
[ScriptOpcode.BRANCH]: (state) => {
[ScriptOpcode.BRANCH]: state => {
state.pc += state.intOperand;
},
[ScriptOpcode.BRANCH_NOT]: (state) => {
[ScriptOpcode.BRANCH_NOT]: state => {
const b = state.popInt();
const a = state.popInt();
@ -161,7 +161,7 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.BRANCH_EQUALS]: (state) => {
[ScriptOpcode.BRANCH_EQUALS]: state => {
const b = state.popInt();
const a = state.popInt();
@ -170,7 +170,7 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.BRANCH_LESS_THAN]: (state) => {
[ScriptOpcode.BRANCH_LESS_THAN]: state => {
const b = state.popInt();
const a = state.popInt();
@ -179,7 +179,7 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.BRANCH_GREATER_THAN]: (state) => {
[ScriptOpcode.BRANCH_GREATER_THAN]: state => {
const b = state.popInt();
const a = state.popInt();
@ -188,7 +188,7 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.BRANCH_LESS_THAN_OR_EQUALS]: (state) => {
[ScriptOpcode.BRANCH_LESS_THAN_OR_EQUALS]: state => {
const b = state.popInt();
const a = state.popInt();
@ -197,7 +197,7 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.BRANCH_GREATER_THAN_OR_EQUALS]: (state) => {
[ScriptOpcode.BRANCH_GREATER_THAN_OR_EQUALS]: state => {
const b = state.popInt();
const a = state.popInt();
@ -206,15 +206,15 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.POP_INT_DISCARD]: (state) => {
[ScriptOpcode.POP_INT_DISCARD]: state => {
state.isp--;
},
[ScriptOpcode.POP_STRING_DISCARD]: (state) => {
[ScriptOpcode.POP_STRING_DISCARD]: state => {
state.ssp--;
},
[ScriptOpcode.RETURN]: (state) => {
[ScriptOpcode.RETURN]: state => {
if (state.fp === 0) {
state.execution = ScriptState.FINISHED;
return;
@ -227,7 +227,7 @@ const CoreOps: CommandHandlers = {
state.stringLocals = frame.stringLocals;
},
[ScriptOpcode.JOIN_STRING]: (state) => {
[ScriptOpcode.JOIN_STRING]: state => {
const count = state.intOperand;
const strings = [];
@ -238,35 +238,35 @@ const CoreOps: CommandHandlers = {
state.pushString(strings.reverse().join(''));
},
[ScriptOpcode.GOSUB]: (state) => {
[ScriptOpcode.GOSUB]: state => {
gosub(state, state.popInt());
},
[ScriptOpcode.GOSUB_WITH_PARAMS]: (state) => {
[ScriptOpcode.GOSUB_WITH_PARAMS]: state => {
gosub(state, state.intOperand);
},
[ScriptOpcode.JUMP]: (state) => {
[ScriptOpcode.JUMP]: state => {
jump(state, state.popInt());
},
[ScriptOpcode.JUMP_WITH_PARAMS]: (state) => {
[ScriptOpcode.JUMP_WITH_PARAMS]: state => {
jump(state, state.intOperand);
},
[ScriptOpcode.DEFINE_ARRAY]: (state) => {
[ScriptOpcode.DEFINE_ARRAY]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.PUSH_ARRAY_INT]: (state) => {
[ScriptOpcode.PUSH_ARRAY_INT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.POP_ARRAY_INT]: (state) => {
[ScriptOpcode.POP_ARRAY_INT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.SWITCH]: (state) => {
[ScriptOpcode.SWITCH]: state => {
const key = state.popInt();
const table = state.script.switchTables[state.intOperand];
if (table === undefined) {
@ -279,16 +279,16 @@ const CoreOps: CommandHandlers = {
}
},
[ScriptOpcode.PUSH_VARS]: (state) => {
const vars = state.intOperand & 0xFFFF;
[ScriptOpcode.PUSH_VARS]: state => {
const vars = state.intOperand & 0xffff;
state.pushInt(World.vars[vars]);
},
[ScriptOpcode.POP_VARS]: (state) => {
const vars = state.intOperand & 0xFFFF;
[ScriptOpcode.POP_VARS]: state => {
const vars = state.intOperand & 0xffff;
const value = state.popInt();
World.vars[vars] = value;
},
}
};
export default CoreOps;

View file

@ -6,11 +6,11 @@ import ScriptOpcode from '#lostcity/engine/script/ScriptOpcode.js';
import { CommandHandlers } from '#lostcity/engine/script/ScriptRunner.js';
const DebugOps: CommandHandlers = {
[ScriptOpcode.DB_FIND_WITH_COUNT]: (state) => {
[ScriptOpcode.DB_FIND_WITH_COUNT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.DB_FINDNEXT]: (state) => {
[ScriptOpcode.DB_FINDNEXT]: state => {
if (!state.dbTable) {
throw new Error('No table selected');
}
@ -26,12 +26,12 @@ const DebugOps: CommandHandlers = {
state.pushInt(row.id);
},
[ScriptOpcode.DB_GETFIELD]: (state) => {
[ScriptOpcode.DB_GETFIELD]: state => {
const [row, tableColumnPacked, listIndex] = state.popInts(3);
const table = (tableColumnPacked >> 12) & 0xFFFF;
const column = (tableColumnPacked >> 4) & 0x7F;
const tuple = tableColumnPacked & 0x3F;
const table = (tableColumnPacked >> 12) & 0xffff;
const column = (tableColumnPacked >> 4) & 0x7f;
const tuple = tableColumnPacked & 0x3f;
const rowType = DbRowType.get(row);
const tableType = DbTableType.get(table);
@ -53,12 +53,12 @@ const DebugOps: CommandHandlers = {
}
},
[ScriptOpcode.DB_GETFIELDCOUNT]: (state) => {
[ScriptOpcode.DB_GETFIELDCOUNT]: state => {
const [row, tableColumnPacked] = state.popInts(2);
const table = (tableColumnPacked >> 12) & 0xFFFF;
const column = (tableColumnPacked >> 4) & 0x7F;
const tuple = tableColumnPacked & 0x3F;
const table = (tableColumnPacked >> 12) & 0xffff;
const column = (tableColumnPacked >> 4) & 0x7f;
const tuple = tableColumnPacked & 0x3f;
const rowType = DbRowType.get(row);
const tableType = DbTableType.get(table);
@ -71,33 +71,33 @@ const DebugOps: CommandHandlers = {
state.pushInt(rowType.columnValues[column].length / tableType.types[column].length);
},
[ScriptOpcode.DB_LISTALL_WITH_COUNT]: (state) => {
[ScriptOpcode.DB_LISTALL_WITH_COUNT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.DB_GETROWTABLE]: (state) => {
[ScriptOpcode.DB_GETROWTABLE]: state => {
const row = state.popInt();
const rowType = DbRowType.get(row);
state.pushInt(rowType.tableId);
},
[ScriptOpcode.DB_FINDBYINDEX]: (state) => {
[ScriptOpcode.DB_FINDBYINDEX]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.DB_FIND_REFINE_WITH_COUNT]: (state) => {
[ScriptOpcode.DB_FIND_REFINE_WITH_COUNT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.DB_FIND]: (state) => {
[ScriptOpcode.DB_FIND]: state => {
const isString = state.popInt() == 2;
const query = isString ? state.popString() : state.popInt();
const tableColumnPacked = state.popInt();
const table = (tableColumnPacked >> 12) & 0xFFFF;
const column = (tableColumnPacked >> 4) & 0x7F;
const tuple = tableColumnPacked & 0x3F;
const table = (tableColumnPacked >> 12) & 0xffff;
const column = (tableColumnPacked >> 4) & 0x7f;
const tuple = tableColumnPacked & 0x3f;
state.dbTable = DbTableType.get(table);
state.dbRow = -1;
@ -116,14 +116,14 @@ const DebugOps: CommandHandlers = {
state.pushInt(state.dbRowQuery.length);
},
[ScriptOpcode.DB_FIND_REFINE]: (state) => {
[ScriptOpcode.DB_FIND_REFINE]: state => {
const isString = state.popInt() == 2;
const query = isString ? state.popString() : state.popInt();
const tableColumnPacked = state.popInt();
const table = (tableColumnPacked >> 12) & 0xFFFF;
const column = (tableColumnPacked >> 4) & 0x7F;
const tuple = tableColumnPacked & 0x3F;
const table = (tableColumnPacked >> 12) & 0xffff;
const column = (tableColumnPacked >> 4) & 0x7f;
const tuple = tableColumnPacked & 0x3f;
// ----
@ -150,9 +150,9 @@ const DebugOps: CommandHandlers = {
state.pushInt(state.dbRowQuery.length);
},
[ScriptOpcode.DB_LISTALL]: (state) => {
[ScriptOpcode.DB_LISTALL]: state => {
throw new Error('unimplemented');
},
}
};
export default DebugOps;

View file

@ -4,13 +4,13 @@ import { CommandHandlers } from '#lostcity/engine/script/ScriptRunner.js';
import Environment from '#lostcity/util/Environment.js';
const DebugOps: CommandHandlers = {
[ScriptOpcode.ERROR]: (state) => {
[ScriptOpcode.ERROR]: state => {
throw new Error(state.popString());
},
[ScriptOpcode.MAP_LOCALDEV]: (state) => {
[ScriptOpcode.MAP_LOCALDEV]: state => {
state.pushInt(Environment.LOCAL_DEV ? 1 : 0);
},
}
};
export default DebugOps;

View file

@ -4,7 +4,7 @@ import ScriptOpcode from '#lostcity/engine/script/ScriptOpcode.js';
import { CommandHandlers } from '#lostcity/engine/script/ScriptRunner.js';
const EnumOps: CommandHandlers = {
[ScriptOpcode.ENUM]: (state) => {
[ScriptOpcode.ENUM]: state => {
const [inputType, outputType, enumId, key] = state.popInts(4);
const enumType = EnumType.get(enumId);
@ -21,12 +21,12 @@ const EnumOps: CommandHandlers = {
}
},
[ScriptOpcode.ENUM_GETOUTPUTCOUNT]: (state) => {
[ScriptOpcode.ENUM_GETOUTPUTCOUNT]: state => {
const enumId = state.popInt();
const enumType = EnumType.get(enumId);
state.pushInt(enumType.values.size);
},
}
};
export default EnumOps;

View file

@ -16,7 +16,7 @@ const ProtectedActivePlayer = [ScriptPointer.ProtectedActivePlayer, ScriptPointe
const InvOps: CommandHandlers = {
// inv config
[ScriptOpcode.INV_ALLSTOCK]: (state) => {
[ScriptOpcode.INV_ALLSTOCK]: state => {
const inv = state.popInt();
if (inv === -1) {
@ -28,7 +28,7 @@ const InvOps: CommandHandlers = {
},
// inv config
[ScriptOpcode.INV_SIZE]: (state) => {
[ScriptOpcode.INV_SIZE]: state => {
const inv = state.popInt();
if (inv === -1) {
@ -40,7 +40,7 @@ const InvOps: CommandHandlers = {
},
// inv config
[ScriptOpcode.INV_STOCKBASE]: (state) => {
[ScriptOpcode.INV_STOCKBASE]: state => {
const [inv, obj] = state.popInts(2);
if (inv === -1) {
@ -57,7 +57,7 @@ const InvOps: CommandHandlers = {
},
// inv write
[ScriptOpcode.INV_ADD]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_ADD]: checkedHandler(ActivePlayer, state => {
const [inv, objId, count] = state.popInts(3);
if (inv === -1) {
@ -89,26 +89,20 @@ const InvOps: CommandHandlers = {
const player = state.activePlayer;
const overflow = count - player.invAdd(inv, objId, count);
if (overflow > 0) {
const floorObj = new Obj(
player.level,
player.x,
player.z,
objId,
overflow
);
const floorObj = new Obj(player.level, player.x, player.z, objId, overflow);
World.addObj(floorObj, player, 200);
}
}),
// inv write
[ScriptOpcode.INV_CHANGESLOT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_CHANGESLOT]: checkedHandler(ActivePlayer, state => {
const [inv, find, replace, replaceCount] = state.popInts(4);
throw new Error('unimplemented');
}),
// inv write
[ScriptOpcode.INV_CLEAR]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_CLEAR]: checkedHandler(ActivePlayer, state => {
const inv = state.popInt();
if (inv === -1) {
@ -124,7 +118,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_DEL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_DEL]: checkedHandler(ActivePlayer, state => {
const [inv, obj, count] = state.popInts(3);
if (inv === -1) {
@ -148,7 +142,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_DELSLOT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_DELSLOT]: checkedHandler(ActivePlayer, state => {
const [inv, slot] = state.popInts(2);
if (inv === -1) {
@ -173,7 +167,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_DROPITEM]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_DROPITEM]: checkedHandler(ActivePlayer, state => {
const [inv, coord, obj, count, duration] = state.popInts(5);
if (inv === -1) {
@ -217,7 +211,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_DROPSLOT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_DROPSLOT]: checkedHandler(ActivePlayer, state => {
const [inv, coord, slot, duration] = state.popInts(4);
if (inv === -1) {
@ -262,7 +256,7 @@ const InvOps: CommandHandlers = {
}),
// inv read
[ScriptOpcode.INV_FREESPACE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_FREESPACE]: checkedHandler(ActivePlayer, state => {
const inv = state.popInt();
if (inv === -1) {
@ -273,7 +267,7 @@ const InvOps: CommandHandlers = {
}),
// inv read
[ScriptOpcode.INV_GETNUM]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_GETNUM]: checkedHandler(ActivePlayer, state => {
const [inv, slot] = state.popInts(2);
if (inv === -1) {
@ -290,7 +284,7 @@ const InvOps: CommandHandlers = {
}),
// inv read
[ScriptOpcode.INV_GETOBJ]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_GETOBJ]: checkedHandler(ActivePlayer, state => {
const [inv, slot] = state.popInts(2);
const obj = state.activePlayer.invGetSlot(inv, slot);
@ -298,7 +292,7 @@ const InvOps: CommandHandlers = {
}),
// inv read
[ScriptOpcode.INV_ITEMSPACE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_ITEMSPACE]: checkedHandler(ActivePlayer, state => {
const [inv, obj, count, size] = state.popInts(4);
if (inv === -1) {
@ -322,7 +316,7 @@ const InvOps: CommandHandlers = {
}),
// inv read
[ScriptOpcode.INV_ITEMSPACE2]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_ITEMSPACE2]: checkedHandler(ActivePlayer, state => {
const [inv, obj, count, size] = state.popInts(4);
if (inv === -1) {
@ -341,7 +335,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_MOVEFROMSLOT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_MOVEFROMSLOT]: checkedHandler(ActivePlayer, state => {
const [fromInv, toInv, fromSlot] = state.popInts(3);
if (fromInv === -1) {
@ -367,22 +361,16 @@ const InvOps: CommandHandlers = {
}
const player = state.activePlayer;
const {overflow, fromObj} = player.invMoveFromSlot(fromInv, toInv, fromSlot);
const { overflow, fromObj } = player.invMoveFromSlot(fromInv, toInv, fromSlot);
if (overflow > 0) {
const floorObj = new Obj(
player.level,
player.x,
player.z,
fromObj,
overflow
);
const floorObj = new Obj(player.level, player.x, player.z, fromObj, overflow);
World.addObj(floorObj, player, 200);
}
}),
// inv write
[ScriptOpcode.INV_MOVETOSLOT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_MOVETOSLOT]: checkedHandler(ActivePlayer, state => {
const [fromInv, toInv, fromSlot, toSlot] = state.popInts(4);
if (fromInv === -1) {
@ -415,7 +403,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.BOTH_MOVEINV]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.BOTH_MOVEINV]: checkedHandler(ActivePlayer, state => {
const [from, to] = state.popInts(2);
if (from === -1) {
@ -472,7 +460,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_MOVEITEM]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_MOVEITEM]: checkedHandler(ActivePlayer, state => {
const [fromInv, toInv, obj, count] = state.popInts(4);
if (fromInv === -1) {
@ -510,7 +498,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_MOVEITEM_CERT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_MOVEITEM_CERT]: checkedHandler(ActivePlayer, state => {
const [fromInv, toInv, obj, count] = state.popInts(4);
if (fromInv === -1) {
@ -553,7 +541,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_MOVEITEM_UNCERT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_MOVEITEM_UNCERT]: checkedHandler(ActivePlayer, state => {
const [fromInv, toInv, obj, count] = state.popInts(4);
if (fromInv === -1) {
@ -596,7 +584,7 @@ const InvOps: CommandHandlers = {
}),
// inv write
[ScriptOpcode.INV_SETSLOT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_SETSLOT]: checkedHandler(ActivePlayer, state => {
const [inv, slot, objId, count] = state.popInts(4);
if (inv === -1) {
@ -633,7 +621,7 @@ const InvOps: CommandHandlers = {
}),
// inv read
[ScriptOpcode.INV_TOTAL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_TOTAL]: checkedHandler(ActivePlayer, state => {
const [inv, obj] = state.popInts(2);
if (inv === -1) {
@ -650,7 +638,7 @@ const InvOps: CommandHandlers = {
}),
// inv read
[ScriptOpcode.INV_TOTALCAT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_TOTALCAT]: checkedHandler(ActivePlayer, state => {
const [inv, category] = state.popInts(2);
if (inv === -1) {
@ -665,7 +653,7 @@ const InvOps: CommandHandlers = {
}),
// inv protocol
[ScriptOpcode.INV_TRANSMIT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_TRANSMIT]: checkedHandler(ActivePlayer, state => {
const [inv, com] = state.popInts(2);
if (inv === -1) {
@ -680,7 +668,7 @@ const InvOps: CommandHandlers = {
}),
// inv protocol
[ScriptOpcode.INVOTHER_TRANSMIT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INVOTHER_TRANSMIT]: checkedHandler(ActivePlayer, state => {
const [uid, inv, com] = state.popInts(3);
if (uid === -1) {
@ -699,7 +687,7 @@ const InvOps: CommandHandlers = {
}),
// inv protocol
[ScriptOpcode.INV_STOPTRANSMIT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.INV_STOPTRANSMIT]: checkedHandler(ActivePlayer, state => {
const com = state.popInt();
if (com === -1) {
@ -707,7 +695,7 @@ const InvOps: CommandHandlers = {
}
state.activePlayer.invStopListenOnCom(com);
}),
})
};
export default InvOps;

View file

@ -6,7 +6,7 @@ import ScriptOpcode from '#lostcity/engine/script/ScriptOpcode.js';
import { CommandHandlers } from '#lostcity/engine/script/ScriptRunner.js';
const LocConfigOps: CommandHandlers = {
[ScriptOpcode.LC_NAME]: (state) => {
[ScriptOpcode.LC_NAME]: state => {
const locId = state.popInt();
if (locId == -1) {
@ -18,7 +18,7 @@ const LocConfigOps: CommandHandlers = {
state.pushString(locType.name ?? locType.debugname ?? 'null');
},
[ScriptOpcode.LC_PARAM]: (state) => {
[ScriptOpcode.LC_PARAM]: state => {
const [locId, paramId] = state.popInts(2);
if (locId == -1) {
@ -38,8 +38,8 @@ const LocConfigOps: CommandHandlers = {
}
},
[ScriptOpcode.LC_CATEGORY]: (state) => {
const locId= state.popInt();
[ScriptOpcode.LC_CATEGORY]: state => {
const locId = state.popInt();
if (locId == -1) {
throw new Error(`attempted to use obj with id: ${locId}`);
@ -50,7 +50,7 @@ const LocConfigOps: CommandHandlers = {
state.pushInt(locType.category);
},
[ScriptOpcode.LC_DESC]: (state) => {
[ScriptOpcode.LC_DESC]: state => {
const locId = state.popInt();
if (locId == -1) {
@ -62,7 +62,7 @@ const LocConfigOps: CommandHandlers = {
state.pushString(locType.desc ?? 'null');
},
[ScriptOpcode.LC_DEBUGNAME]: (state) => {
[ScriptOpcode.LC_DEBUGNAME]: state => {
const locId = state.popInt();
if (locId == -1) {
@ -72,7 +72,7 @@ const LocConfigOps: CommandHandlers = {
const locType = LocType.get(locId);
state.pushString(locType.debugname ?? 'null');
},
}
};
export default LocConfigOps;

View file

@ -17,7 +17,7 @@ let locFindAllZone: Loc[] = [];
let locFindAllZoneIndex = 0;
const LocOps: CommandHandlers = {
[ScriptOpcode.LOC_ADD]: (state) => {
[ScriptOpcode.LOC_ADD]: state => {
const [coord, type, angle, shape, duration] = state.popInts(5);
if (type == -1) {
@ -36,46 +36,37 @@ const LocOps: CommandHandlers = {
throw new Error(`attempted to use angle that was out of range: ${angle}. Range should be: 0 to 3`);
}
if (shape < 0 || shape > 0x1F) {
if (shape < 0 || shape > 0x1f) {
throw new Error(`attempted to use shape that was out of range: ${shape}. Range should be: 0 to 31`);
}
const pos = Position.unpackCoord(coord);
const locType = LocType.get(type);
const loc = new Loc(
pos.level,
pos.x,
pos.z,
locType.width,
locType.length,
type,
shape,
angle
);
const loc = new Loc(pos.level, pos.x, pos.z, locType.width, locType.length, type, shape, angle);
World.addLoc(loc, duration);
state.activeLoc = loc;
state.pointerAdd(ActiveLoc[state.intOperand]);
},
[ScriptOpcode.LOC_ANGLE]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_ANGLE]: checkedHandler(ActiveLoc, state => {
state.pushInt(state.activeLoc.angle);
}),
[ScriptOpcode.LOC_ANIM]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_ANIM]: checkedHandler(ActiveLoc, state => {
const seq = state.popInt();
const loc = state.activeLoc;
World.getZone(loc.x, loc.z, loc.level).animLoc(loc, seq);
}),
[ScriptOpcode.LOC_CATEGORY]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_CATEGORY]: checkedHandler(ActiveLoc, state => {
const loc = LocType.get(state.activeLoc.type);
state.pushInt(loc.category);
}),
[ScriptOpcode.LOC_CHANGE]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_CHANGE]: checkedHandler(ActiveLoc, state => {
// Not proper implementation Polar said, do proper later Pazaz
const [newLoc, duration] = state.popInts(2);
@ -85,29 +76,19 @@ const LocOps: CommandHandlers = {
}
const locType = LocType.get(newLoc);
const loc = new Loc(
state.activeLoc.level,
state.activeLoc.x,
state.activeLoc.z,
locType.width,
locType.length,
newLoc,
state.activeLoc.shape,
state.activeLoc.angle
);
const loc = new Loc(state.activeLoc.level, state.activeLoc.x, state.activeLoc.z, locType.width, locType.length, newLoc, state.activeLoc.shape, state.activeLoc.angle);
World.addLoc(loc, duration);
state.activeLoc = loc;
state.pointerAdd(ActiveLoc[state.intOperand]);
}),
[ScriptOpcode.LOC_COORD]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_COORD]: checkedHandler(ActiveLoc, state => {
const loc = state.activeLoc;
state.pushInt(Position.packCoord(loc.level, loc.x, loc.z));
}),
[ScriptOpcode.LOC_DEL]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_DEL]: checkedHandler(ActiveLoc, state => {
const duration = state.popInt();
if (duration < 1) {
@ -117,8 +98,8 @@ const LocOps: CommandHandlers = {
World.removeLoc(state.activeLoc, duration);
}),
[ScriptOpcode.LOC_FIND]: (state) => {
const [ coord, locId ] = state.popInts(2);
[ScriptOpcode.LOC_FIND]: state => {
const [coord, locId] = state.popInts(2);
if (coord < 0 || coord > Position.max) {
throw new Error(`attempted to use coord that was out of range: ${coord}. Range should be: 0 to ${Position.max}`);
@ -136,7 +117,7 @@ const LocOps: CommandHandlers = {
state.pushInt(1);
},
[ScriptOpcode.LOC_FINDALLZONE]: (state) => {
[ScriptOpcode.LOC_FINDALLZONE]: state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -155,7 +136,7 @@ const LocOps: CommandHandlers = {
}
},
[ScriptOpcode.LOC_FINDNEXT]: (state) => {
[ScriptOpcode.LOC_FINDNEXT]: state => {
const loc = locFindAllZone[locFindAllZoneIndex++];
if (!loc) {
state.pushInt(0);
@ -167,7 +148,7 @@ const LocOps: CommandHandlers = {
state.pushInt(1);
},
[ScriptOpcode.LOC_PARAM]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_PARAM]: checkedHandler(ActiveLoc, state => {
const paramId = state.popInt();
const param = ParamType.get(paramId);
const loc = LocType.get(state.activeLoc.type);
@ -178,19 +159,19 @@ const LocOps: CommandHandlers = {
}
}),
[ScriptOpcode.LOC_TYPE]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_TYPE]: checkedHandler(ActiveLoc, state => {
state.pushInt(state.activeLoc.type);
}),
[ScriptOpcode.LOC_NAME]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_NAME]: checkedHandler(ActiveLoc, state => {
const loc = LocType.get(state.activeLoc.type);
state.pushString(loc.name ?? 'null');
}),
[ScriptOpcode.LOC_SHAPE]: checkedHandler(ActiveLoc, (state) => {
[ScriptOpcode.LOC_SHAPE]: checkedHandler(ActiveLoc, state => {
state.pushInt(state.activeLoc.shape);
}),
})
};
export default LocOps;

View file

@ -6,7 +6,7 @@ import ScriptOpcode from '#lostcity/engine/script/ScriptOpcode.js';
import { CommandHandlers } from '#lostcity/engine/script/ScriptRunner.js';
const NpcConfigOps: CommandHandlers = {
[ScriptOpcode.NC_NAME]: (state) => {
[ScriptOpcode.NC_NAME]: state => {
const npcId = state.popInt();
if (npcId == -1) {
@ -18,7 +18,7 @@ const NpcConfigOps: CommandHandlers = {
state.pushString(npcType.name ?? npcType.debugname ?? 'null');
},
[ScriptOpcode.NC_PARAM]: (state) => {
[ScriptOpcode.NC_PARAM]: state => {
const [npcId, paramId] = state.popInts(2);
if (npcId == -1) {
@ -38,8 +38,8 @@ const NpcConfigOps: CommandHandlers = {
}
},
[ScriptOpcode.NC_CATEGORY]: (state) => {
const npcId= state.popInt();
[ScriptOpcode.NC_CATEGORY]: state => {
const npcId = state.popInt();
if (npcId == -1) {
throw new Error(`attempted to use npc with id: ${npcId}`);
@ -50,7 +50,7 @@ const NpcConfigOps: CommandHandlers = {
state.pushInt(npcType.category);
},
[ScriptOpcode.NC_DESC]: (state) => {
[ScriptOpcode.NC_DESC]: state => {
const npcId = state.popInt();
if (npcId == -1) {
@ -62,7 +62,7 @@ const NpcConfigOps: CommandHandlers = {
state.pushString(npcType.desc ?? 'null');
},
[ScriptOpcode.NC_DEBUGNAME]: (state) => {
[ScriptOpcode.NC_DEBUGNAME]: state => {
const npcId = state.popInt();
if (npcId == -1) {
@ -74,7 +74,7 @@ const NpcConfigOps: CommandHandlers = {
state.pushString(npcType.debugname ?? 'null');
},
[ScriptOpcode.NC_OP]: (state) => {
[ScriptOpcode.NC_OP]: state => {
const [npcId, op] = state.popInts(2);
if (npcId == -1) {
throw new Error(`attempted to use npc with id: ${npcId}`);

View file

@ -26,10 +26,10 @@ const ActiveNpc = [ScriptPointer.ActiveNpc, ScriptPointer.ActiveNpc2];
let npcFindResults: IterableIterator<[number, number]>;
const NpcOps: CommandHandlers = {
[ScriptOpcode.NPC_FINDUID]: (state) => {
[ScriptOpcode.NPC_FINDUID]: state => {
const npcUid = state.popInt();
const slot = npcUid & 0xFFFF;
const expectedType = npcUid >> 16 & 0xFFFF;
const slot = npcUid & 0xffff;
const expectedType = (npcUid >> 16) & 0xffff;
const npc = World.getNpc(slot);
if (!npc || npc.type !== expectedType) {
@ -42,7 +42,7 @@ const NpcOps: CommandHandlers = {
state.pushInt(1);
},
[ScriptOpcode.NPC_ADD]: (state) => {
[ScriptOpcode.NPC_ADD]: state => {
const [coord, id, duration] = state.popInts(3);
if (id == -1) {
@ -60,17 +60,7 @@ const NpcOps: CommandHandlers = {
const pos = Position.unpackCoord(coord);
const npcType = NpcType.get(id);
const npc = new Npc(
pos.level,
pos.x,
pos.z,
npcType.size,
npcType.size,
World.getNextNid(),
npcType.id,
npcType.moverestrict,
npcType.blockwalk
);
const npc = new Npc(pos.level, pos.x, pos.z, npcType.size, npcType.size, World.getNextNid(), npcType.id, npcType.moverestrict, npcType.blockwalk);
npc.static = false;
npc.despawn = World.currentTick + duration;
@ -79,29 +69,29 @@ const NpcOps: CommandHandlers = {
state.pointerAdd(ActiveNpc[state.intOperand]);
},
[ScriptOpcode.NPC_ANIM]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_ANIM]: checkedHandler(ActiveNpc, state => {
const delay = state.popInt();
const seq = state.popInt();
state.activeNpc.playAnimation(seq, delay);
}),
[ScriptOpcode.NPC_BASESTAT]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_BASESTAT]: checkedHandler(ActiveNpc, state => {
const stat = state.popInt();
state.pushInt(state.activeNpc.baseLevels[stat]);
}),
[ScriptOpcode.NPC_CATEGORY]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_CATEGORY]: checkedHandler(ActiveNpc, state => {
const npc = NpcType.get(state.activeNpc.type);
state.pushInt(npc.category);
}),
[ScriptOpcode.NPC_COORD]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_COORD]: checkedHandler(ActiveNpc, state => {
const npc = state.activeNpc;
state.pushInt(Position.packCoord(npc.level, npc.x, npc.z));
}),
[ScriptOpcode.NPC_DEL]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_DEL]: checkedHandler(ActiveNpc, state => {
if (Environment.CLIRUNNER) {
return;
}
@ -109,7 +99,7 @@ const NpcOps: CommandHandlers = {
World.removeNpc(state.activeNpc);
}),
[ScriptOpcode.NPC_DELAY]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_DELAY]: checkedHandler(ActiveNpc, state => {
if (Environment.CLIRUNNER) {
return;
}
@ -118,7 +108,7 @@ const NpcOps: CommandHandlers = {
state.execution = ScriptState.NPC_SUSPENDED;
}),
[ScriptOpcode.NPC_FACESQUARE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_FACESQUARE]: checkedHandler(ActiveNpc, state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -130,11 +120,11 @@ const NpcOps: CommandHandlers = {
state.activeNpc.faceSquare(pos.x, pos.z);
}),
[ScriptOpcode.NPC_FINDEXACT]: (state) => {
[ScriptOpcode.NPC_FINDEXACT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.NPC_FINDHERO]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_FINDHERO]: checkedHandler(ActiveNpc, state => {
const uid = state.activeNpc.findHero();
if (uid === -1) {
state.pushInt(0);
@ -152,7 +142,7 @@ const NpcOps: CommandHandlers = {
state.pushInt(1);
}),
[ScriptOpcode.NPC_PARAM]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_PARAM]: checkedHandler(ActiveNpc, state => {
const paramId = state.popInt();
const param = ParamType.get(paramId);
const npc = NpcType.get(state.activeNpc.type);
@ -163,7 +153,7 @@ const NpcOps: CommandHandlers = {
}
}),
[ScriptOpcode.NPC_QUEUE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_QUEUE]: checkedHandler(ActiveNpc, state => {
const delay = state.popInt();
const arg = state.popInt();
const queueId = state.popInt() - 1;
@ -178,7 +168,7 @@ const NpcOps: CommandHandlers = {
}
}),
[ScriptOpcode.NPC_RANGE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_RANGE]: checkedHandler(ActiveNpc, state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -191,30 +181,37 @@ const NpcOps: CommandHandlers = {
if (pos.level !== npc.level) {
state.pushInt(-1);
} else {
state.pushInt(Position.distanceTo(npc, { x: pos.x, z: pos.z, width: 1, length: 1 }));
state.pushInt(
Position.distanceTo(npc, {
x: pos.x,
z: pos.z,
width: 1,
length: 1
})
);
}
}),
[ScriptOpcode.NPC_SAY]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_SAY]: checkedHandler(ActiveNpc, state => {
state.activeNpc.say(state.popString());
}),
[ScriptOpcode.NPC_SETHUNT]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_SETHUNT]: checkedHandler(ActiveNpc, state => {
throw new Error('unimplemented');
}),
[ScriptOpcode.NPC_SETHUNTMODE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_SETHUNTMODE]: checkedHandler(ActiveNpc, state => {
const mode = state.popInt();
if (mode === -1) {
throw new Error('attempted to use a hunt mode type that was null.');
}
const huntType = HuntType.get(mode);
const huntType = HuntType.get(mode);
state.activeNpc.huntMode = huntType.id;
}),
[ScriptOpcode.NPC_SETMODE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_SETMODE]: checkedHandler(ActiveNpc, state => {
const mode = state.popInt();
if (mode > NpcMode.APNPC5) {
@ -247,12 +244,12 @@ const NpcOps: CommandHandlers = {
}
}),
[ScriptOpcode.NPC_STAT]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_STAT]: checkedHandler(ActiveNpc, state => {
const stat = state.popInt();
state.pushInt(state.activeNpc.levels[stat]);
}),
[ScriptOpcode.NPC_STATHEAL]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_STATHEAL]: checkedHandler(ActiveNpc, state => {
const [stat, constant, percent] = state.popInts(3);
const npc = state.activeNpc;
@ -267,35 +264,35 @@ const NpcOps: CommandHandlers = {
}
}),
[ScriptOpcode.NPC_TYPE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_TYPE]: checkedHandler(ActiveNpc, state => {
state.pushInt(state.activeNpc.type);
}),
[ScriptOpcode.NPC_DAMAGE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_DAMAGE]: checkedHandler(ActiveNpc, state => {
const amount = state.popInt();
const type = state.popInt();
state.activeNpc.applyDamage(amount, type);
}),
[ScriptOpcode.NPC_NAME]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_NAME]: checkedHandler(ActiveNpc, state => {
const npcType = NpcType.get(state.activeNpc.type);
state.pushString(npcType.name ?? 'null');
}),
[ScriptOpcode.NPC_UID]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_UID]: checkedHandler(ActiveNpc, state => {
const npc = state.activeNpc;
state.pushInt(npc.uid);
}),
[ScriptOpcode.NPC_SETTIMER]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_SETTIMER]: checkedHandler(ActiveNpc, state => {
const interval = state.popInt();
state.activeNpc.setTimer(interval);
}),
[ScriptOpcode.SPOTANIM_NPC]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.SPOTANIM_NPC]: checkedHandler(ActiveNpc, state => {
const delay = state.popInt();
const height = state.popInt();
const spotanim = state.popInt();
@ -303,7 +300,7 @@ const NpcOps: CommandHandlers = {
state.activeNpc.spotanim(spotanim, height, delay);
}),
[ScriptOpcode.NPC_FINDALLZONE]: (state) => {
[ScriptOpcode.NPC_FINDALLZONE]: state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -321,7 +318,7 @@ const NpcOps: CommandHandlers = {
}
},
[ScriptOpcode.NPC_FINDNEXT]: (state) => {
[ScriptOpcode.NPC_FINDNEXT]: state => {
const result = npcFindResults.next();
if (result.done) {
// no more npcs in zone
@ -341,7 +338,7 @@ const NpcOps: CommandHandlers = {
state.pushInt(1);
},
[ScriptOpcode.NPC_TELE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_TELE]: checkedHandler(ActiveNpc, state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -353,7 +350,7 @@ const NpcOps: CommandHandlers = {
state.activeNpc.teleport(pos.x, pos.z, pos.level);
}),
[ScriptOpcode.NPC_WALK]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_WALK]: checkedHandler(ActiveNpc, state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -366,26 +363,26 @@ const NpcOps: CommandHandlers = {
npc.queueWaypoint(pos.x, pos.z);
}),
[ScriptOpcode.NPC_CHANGETYPE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_CHANGETYPE]: checkedHandler(ActiveNpc, state => {
const id = state.popInt();
state.activeNpc.changeType(id);
}),
[ScriptOpcode.NPC_GETMODE]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_GETMODE]: checkedHandler(ActiveNpc, state => {
state.pushInt(state.activeNpc.mode);
}),
[ScriptOpcode.NPC_HEROPOINTS]: checkedHandler([ScriptPointer.ActivePlayer, ...ActiveNpc], (state) => {
[ScriptOpcode.NPC_HEROPOINTS]: checkedHandler([ScriptPointer.ActivePlayer, ...ActiveNpc], state => {
const damage = state.popInt();
state.activeNpc.addHero(state.activePlayer.uid, damage);
}),
[ScriptOpcode.NPC_SETMOVECHECK]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_SETMOVECHECK]: checkedHandler(ActiveNpc, state => {
state.activeNpc.moveCheck = state.popInt();
}),
[ScriptOpcode.NPC_STATADD]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_STATADD]: checkedHandler(ActiveNpc, state => {
const [stat, constant, percent] = state.popInts(3);
const npc = state.activeNpc;
@ -398,14 +395,14 @@ const NpcOps: CommandHandlers = {
}
}),
[ScriptOpcode.NPC_STATSUB]: checkedHandler(ActiveNpc, (state) => {
[ScriptOpcode.NPC_STATSUB]: checkedHandler(ActiveNpc, state => {
const [stat, constant, percent] = state.popInts(3);
const npc = state.activeNpc;
const current = npc.levels[stat];
const subbed = current - (constant + (current * percent) / 100);
npc.levels[stat] = Math.max(subbed, 0);
}),
})
};
export default NpcOps;

View file

@ -4,77 +4,77 @@ import { CommandHandlers } from '#lostcity/engine/script/ScriptRunner.js';
import { bitcount, clearBitRange, MASK, setBitRange } from '#lostcity/util/Numbers.js';
const NumberOps: CommandHandlers = {
[ScriptOpcode.ADD]: (state) => {
[ScriptOpcode.ADD]: state => {
const b = state.popInt();
const a = state.popInt();
state.pushInt(a + b);
},
[ScriptOpcode.SUB]: (state) => {
[ScriptOpcode.SUB]: state => {
const b = state.popInt();
const a = state.popInt();
state.pushInt(a - b);
},
[ScriptOpcode.MULTIPLY]: (state) => {
[ScriptOpcode.MULTIPLY]: state => {
const b = state.popInt();
const a = state.popInt();
state.pushInt(a * b);
},
[ScriptOpcode.DIVIDE]: (state) => {
[ScriptOpcode.DIVIDE]: state => {
const b = state.popInt();
const a = state.popInt();
state.pushInt(a / b);
},
[ScriptOpcode.RANDOM]: (state) => {
[ScriptOpcode.RANDOM]: state => {
const a = state.popInt();
state.pushInt(Math.random() * a);
},
[ScriptOpcode.RANDOMINC]: (state) => {
[ScriptOpcode.RANDOMINC]: state => {
const a = state.popInt();
state.pushInt(Math.random() * (a + 1));
},
[ScriptOpcode.INTERPOLATE]: (state) => {
[ScriptOpcode.INTERPOLATE]: state => {
const [y0, y1, x0, x1, x] = state.popInts(5);
const lerp = Math.floor((y1 - y0) / (x1 - x0)) * (x - x0) + y0;
state.pushInt(lerp);
},
[ScriptOpcode.ADDPERCENT]: (state) => {
[ScriptOpcode.ADDPERCENT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.SETBIT]: (state) => {
[ScriptOpcode.SETBIT]: state => {
const [value, bit] = state.popInts(2);
state.pushInt(value | (1 << bit));
},
[ScriptOpcode.CLEARBIT]: (state) => {
[ScriptOpcode.CLEARBIT]: state => {
const [value, bit] = state.popInts(2);
state.pushInt(value & ~(1 << bit));
},
[ScriptOpcode.TESTBIT]: (state) => {
[ScriptOpcode.TESTBIT]: state => {
const [value, bit] = state.popInts(2);
state.pushInt((value & (1 << bit)) ? 1 : 0);
state.pushInt(value & (1 << bit) ? 1 : 0);
},
[ScriptOpcode.MODULO]: (state) => {
[ScriptOpcode.MODULO]: state => {
const [n1, n2] = state.popInts(2);
state.pushInt(n1 % n2);
},
[ScriptOpcode.POW]: (state) => {
[ScriptOpcode.POW]: state => {
const [base, exponent] = state.popInts(2);
state.pushInt(Math.pow(base, exponent));
},
[ScriptOpcode.INVPOW]: (state) => {
[ScriptOpcode.INVPOW]: state => {
const [n1, n2] = state.popInts(2);
if (n1 === 0 || n2 === 0) {
state.pushInt(0);
@ -99,57 +99,57 @@ const NumberOps: CommandHandlers = {
}
},
[ScriptOpcode.AND]: (state) => {
[ScriptOpcode.AND]: state => {
const [n1, n2] = state.popInts(2);
state.pushInt(n1 & n2);
},
[ScriptOpcode.OR]: (state) => {
[ScriptOpcode.OR]: state => {
const [n1, n2] = state.popInts(2);
state.pushInt(n1 | n2);
},
[ScriptOpcode.MIN]: (state) => {
[ScriptOpcode.MIN]: state => {
const [a, b] = state.popInts(2);
state.pushInt(Math.min(a, b));
},
[ScriptOpcode.MAX]: (state) => {
[ScriptOpcode.MAX]: state => {
const [a, b] = state.popInts(2);
state.pushInt(Math.max(a, b));
},
[ScriptOpcode.SCALE]: (state) => {
[ScriptOpcode.SCALE]: state => {
const [a, b, c] = state.popInts(3);
state.pushInt(a * c / b);
state.pushInt((a * c) / b);
},
[ScriptOpcode.BITCOUNT]: (state) => {
[ScriptOpcode.BITCOUNT]: state => {
state.pushInt(bitcount(state.popInt()));
},
[ScriptOpcode.TOGGLEBIT]: (state) => {
[ScriptOpcode.TOGGLEBIT]: state => {
const [value, bit] = state.popInts(2);
state.pushInt(value ^ (1 << bit));
},
[ScriptOpcode.SETBIT_RANGE]: (state) => {
[ScriptOpcode.SETBIT_RANGE]: state => {
const [num, startBit, endBit] = state.popInts(3);
state.pushInt(setBitRange(num, startBit, endBit));
},
[ScriptOpcode.CLEARBIT_RANGE]: (state) => {
[ScriptOpcode.CLEARBIT_RANGE]: state => {
const [num, startBit, endBit] = state.popInts(3);
state.pushInt(clearBitRange(num, startBit, endBit));
},
[ScriptOpcode.GETBIT_RANGE]: (state) => {
[ScriptOpcode.GETBIT_RANGE]: state => {
const [num, startBit, endBit] = state.popInts(3);
const a = 31 - endBit;
state.pushInt((num << a) >>> (startBit + a));
},
[ScriptOpcode.SETBIT_RANGE_TOINT]: (state) => {
[ScriptOpcode.SETBIT_RANGE_TOINT]: state => {
const [num, value, startBit, endBit] = state.popInts(4);
const clearedBitRange = clearBitRange(num, startBit, endBit);
const maxValue = MASK[endBit - startBit + 1];
@ -160,21 +160,21 @@ const NumberOps: CommandHandlers = {
state.pushInt(clearedBitRange | (assignValue << startBit));
},
[ScriptOpcode.SIN_DEG]: (state) => {
[ScriptOpcode.SIN_DEG]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.COS_DEG]: (state) => {
[ScriptOpcode.COS_DEG]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.ATAN2_DEG]: (state) => {
[ScriptOpcode.ATAN2_DEG]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.ABS]: (state) => {
[ScriptOpcode.ABS]: state => {
state.pushInt(Math.abs(state.popInt()));
},
}
};
export default NumberOps;

View file

@ -6,7 +6,7 @@ import ScriptOpcode from '#lostcity/engine/script/ScriptOpcode.js';
import { CommandHandlers } from '#lostcity/engine/script/ScriptRunner.js';
const ObjConfigOps: CommandHandlers = {
[ScriptOpcode.OC_NAME]: (state) => {
[ScriptOpcode.OC_NAME]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -18,7 +18,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushString(objType.name ?? objType.debugname ?? 'null');
},
[ScriptOpcode.OC_PARAM]: (state) => {
[ScriptOpcode.OC_PARAM]: state => {
const [objId, paramId] = state.popInts(2);
if (objId == -1) {
@ -38,7 +38,7 @@ const ObjConfigOps: CommandHandlers = {
}
},
[ScriptOpcode.OC_CATEGORY]: (state) => {
[ScriptOpcode.OC_CATEGORY]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -50,7 +50,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushInt(objType.category);
},
[ScriptOpcode.OC_DESC]: (state) => {
[ScriptOpcode.OC_DESC]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -62,7 +62,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushString(objType.desc ?? 'null');
},
[ScriptOpcode.OC_MEMBERS]: (state) => {
[ScriptOpcode.OC_MEMBERS]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -74,7 +74,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushInt(objType.members ? 1 : 0);
},
[ScriptOpcode.OC_WEIGHT]: (state) => {
[ScriptOpcode.OC_WEIGHT]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -86,7 +86,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushInt(objType.weight);
},
[ScriptOpcode.OC_WEARPOS]: (state) => {
[ScriptOpcode.OC_WEARPOS]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -98,7 +98,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushInt(objType.wearpos);
},
[ScriptOpcode.OC_WEARPOS2]: (state) => {
[ScriptOpcode.OC_WEARPOS2]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -110,7 +110,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushInt(objType.wearpos2);
},
[ScriptOpcode.OC_WEARPOS3]: (state) => {
[ScriptOpcode.OC_WEARPOS3]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -122,7 +122,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushInt(objType.wearpos3);
},
[ScriptOpcode.OC_COST]: (state) => {
[ScriptOpcode.OC_COST]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -133,7 +133,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushInt(obj?.cost);
},
[ScriptOpcode.OC_TRADEABLE]: (state) => {
[ScriptOpcode.OC_TRADEABLE]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -144,7 +144,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushInt(obj?.tradeable ? 1 : 0);
},
[ScriptOpcode.OC_DEBUGNAME]: (state) => {
[ScriptOpcode.OC_DEBUGNAME]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -156,7 +156,7 @@ const ObjConfigOps: CommandHandlers = {
state.pushString(objType.debugname ?? 'null');
},
[ScriptOpcode.OC_CERT]: (state) => {
[ScriptOpcode.OC_CERT]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -172,7 +172,7 @@ const ObjConfigOps: CommandHandlers = {
}
},
[ScriptOpcode.OC_UNCERT]: (state) => {
[ScriptOpcode.OC_UNCERT]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -188,7 +188,7 @@ const ObjConfigOps: CommandHandlers = {
}
},
[ScriptOpcode.OC_STACKABLE]: (state) => {
[ScriptOpcode.OC_STACKABLE]: state => {
const objId = state.popInt();
if (objId == -1) {
@ -198,7 +198,7 @@ const ObjConfigOps: CommandHandlers = {
const objType = ObjType.get(objId);
state.pushInt(objType.stackable ? 1 : 0);
},
}
};
export default ObjConfigOps;

View file

@ -18,7 +18,7 @@ import Environment from '#lostcity/util/Environment.js';
const ActiveObj = [ScriptPointer.ActiveObj, ScriptPointer.ActiveObj2];
const ObjOps: CommandHandlers = {
[ScriptOpcode.OBJ_ADD]: (state) => {
[ScriptOpcode.OBJ_ADD]: state => {
const [coord, objId, count, duration] = state.popInts(4);
if (objId == -1) {
@ -44,13 +44,7 @@ const ObjOps: CommandHandlers = {
const pos = Position.unpackCoord(coord);
const obj = new Obj(
pos.level,
pos.x,
pos.z,
objId,
count
);
const obj = new Obj(pos.level, pos.x, pos.z, objId, count);
World.addObj(obj, state.activePlayer, duration);
state.activeObj = obj;
@ -61,11 +55,11 @@ const ObjOps: CommandHandlers = {
}
},
[ScriptOpcode.OBJ_ADDALL]: (state) => {
[ScriptOpcode.OBJ_ADDALL]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.OBJ_PARAM]: (state) => {
[ScriptOpcode.OBJ_PARAM]: state => {
const paramId = state.popInt();
const param = ParamType.get(paramId);
@ -77,29 +71,29 @@ const ObjOps: CommandHandlers = {
}
},
[ScriptOpcode.OBJ_NAME]: (state) => {
[ScriptOpcode.OBJ_NAME]: state => {
const obj = ObjType.get(state.activeObj.type);
state.pushString(obj.name ?? obj.debugname ?? 'null');
},
[ScriptOpcode.OBJ_DEL]: (state) => {
[ScriptOpcode.OBJ_DEL]: state => {
World.removeObj(state.activeObj, state.activePlayer);
},
[ScriptOpcode.OBJ_COUNT]: (state) => {
[ScriptOpcode.OBJ_COUNT]: state => {
state.pushInt(state.activeObj.count);
},
[ScriptOpcode.OBJ_TYPE]: (state) => {
[ScriptOpcode.OBJ_TYPE]: state => {
state.pushInt(state.activeObj.type);
},
[ScriptOpcode.OBJ_TAKEITEM]: (state) => {
[ScriptOpcode.OBJ_TAKEITEM]: state => {
const inv = state.popInt();
const obj = state.activeObj;
if (World.getObj(obj.x, obj.z, obj.level, obj.id)){
if (World.getObj(obj.x, obj.z, obj.level, obj.id)) {
const objType = ObjType.get(obj.type);
state.activePlayer.playerLog('Picked up item', objType.debugname as string);
@ -108,10 +102,10 @@ const ObjOps: CommandHandlers = {
}
},
[ScriptOpcode.OBJ_COORD]: (state) => {
[ScriptOpcode.OBJ_COORD]: state => {
const obj = state.activeObj;
state.pushInt(Position.packCoord(obj.level, obj.x, obj.z));
},
}
};
export default ObjOps;

View file

@ -19,7 +19,7 @@ const ActivePlayer = [ScriptPointer.ActivePlayer, ScriptPointer.ActivePlayer2];
const ProtectedActivePlayer = [ScriptPointer.ProtectedActivePlayer, ScriptPointer.ProtectedActivePlayer2];
const PlayerOps: CommandHandlers = {
[ScriptOpcode.FINDUID]: (state) => {
[ScriptOpcode.FINDUID]: state => {
const uid = state.popInt();
const player = World.getPlayerByUid(uid);
@ -33,7 +33,7 @@ const PlayerOps: CommandHandlers = {
state.pushInt(1);
},
[ScriptOpcode.P_FINDUID]: (state) => {
[ScriptOpcode.P_FINDUID]: state => {
const uid = state.popInt();
const player = World.getPlayerByUid(uid);
@ -54,7 +54,7 @@ const PlayerOps: CommandHandlers = {
state.pushInt(1);
},
[ScriptOpcode.STRONGQUEUE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.STRONGQUEUE]: checkedHandler(ActivePlayer, state => {
const args = popScriptArgs(state);
const delay = state.popInt();
const scriptId = state.popInt();
@ -66,7 +66,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.enqueueScript(script, PlayerQueueType.STRONG, delay, args);
}),
[ScriptOpcode.WEAKQUEUE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.WEAKQUEUE]: checkedHandler(ActivePlayer, state => {
const args = popScriptArgs(state);
const delay = state.popInt();
const scriptId = state.popInt();
@ -78,7 +78,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.enqueueScript(script, PlayerQueueType.WEAK, delay, args);
}),
[ScriptOpcode.QUEUE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.QUEUE]: checkedHandler(ActivePlayer, state => {
const args = popScriptArgs(state);
const delay = state.popInt();
const scriptId = state.popInt();
@ -90,23 +90,23 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.enqueueScript(script, PlayerQueueType.NORMAL, delay, args);
}),
[ScriptOpcode.ANIM]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.ANIM]: checkedHandler(ActivePlayer, state => {
const delay = state.popInt();
const seq = state.popInt();
state.activePlayer.playAnimation(seq, delay);
}),
[ScriptOpcode.BUFFER_FULL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.BUFFER_FULL]: checkedHandler(ActivePlayer, state => {
throw new Error('unimplemented');
}),
[ScriptOpcode.BUILDAPPEARANCE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.BUILDAPPEARANCE]: checkedHandler(ActivePlayer, state => {
const inv = state.popInt();
state.activePlayer.generateAppearance(inv);
}),
[ScriptOpcode.CAM_LOOKAT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.CAM_LOOKAT]: checkedHandler(ActivePlayer, state => {
const [coord, speed, height, accel] = state.popInts(4);
if (coord < 0 || coord > Position.max) {
@ -123,7 +123,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.write(ServerProt.CAM_LOOKAT, localX, localZ, speed, height, accel);
}),
[ScriptOpcode.CAM_MOVETO]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.CAM_MOVETO]: checkedHandler(ActivePlayer, state => {
const [coord, speed, height, accel] = state.popInts(4);
if (coord < 0 || coord > Position.max) {
@ -140,26 +140,26 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.write(ServerProt.CAM_MOVETO, localX, localZ, speed, height, accel);
}),
[ScriptOpcode.CAM_SHAKE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.CAM_SHAKE]: checkedHandler(ActivePlayer, state => {
const [type, jitter, amplitude, frequency] = state.popInts(4);
state.activePlayer.write(ServerProt.CAM_SHAKE, type, jitter, amplitude, frequency);
}),
[ScriptOpcode.CAM_RESET]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.CAM_RESET]: checkedHandler(ActivePlayer, state => {
state.activePlayer.write(ServerProt.CAM_RESET);
}),
[ScriptOpcode.COORD]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.COORD]: checkedHandler(ActivePlayer, state => {
const player = state.activePlayer;
state.pushInt(Position.packCoord(player.level, player.x, player.z));
}),
[ScriptOpcode.DISPLAYNAME]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.DISPLAYNAME]: checkedHandler(ActivePlayer, state => {
state.pushString(state.activePlayer.displayName);
}),
[ScriptOpcode.FACESQUARE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.FACESQUARE]: checkedHandler(ActivePlayer, state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -171,24 +171,32 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.faceSquare(pos.x, pos.z);
}),
[ScriptOpcode.IF_CLOSE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_CLOSE]: checkedHandler(ActivePlayer, state => {
state.activePlayer.closeModal();
}),
[ScriptOpcode.LAST_COM]: (state) => {
[ScriptOpcode.LAST_COM]: state => {
state.pushInt(state.activePlayer.lastCom);
},
[ScriptOpcode.LAST_INT]: (state) => {
[ScriptOpcode.LAST_INT]: state => {
state.pushInt(state.activePlayer.lastInt);
},
[ScriptOpcode.LAST_ITEM]: (state) => {
[ScriptOpcode.LAST_ITEM]: state => {
const allowedTriggers = [
ServerTriggerType.OPHELD1, ServerTriggerType.OPHELD2, ServerTriggerType.OPHELD3, ServerTriggerType.OPHELD4, ServerTriggerType.OPHELD5,
ServerTriggerType.OPHELD1,
ServerTriggerType.OPHELD2,
ServerTriggerType.OPHELD3,
ServerTriggerType.OPHELD4,
ServerTriggerType.OPHELD5,
ServerTriggerType.OPHELDU,
ServerTriggerType.OPHELDT,
ServerTriggerType.INV_BUTTON1, ServerTriggerType.INV_BUTTON2, ServerTriggerType.INV_BUTTON3, ServerTriggerType.INV_BUTTON4, ServerTriggerType.INV_BUTTON5
ServerTriggerType.INV_BUTTON1,
ServerTriggerType.INV_BUTTON2,
ServerTriggerType.INV_BUTTON3,
ServerTriggerType.INV_BUTTON4,
ServerTriggerType.INV_BUTTON5
];
if (!allowedTriggers.includes(state.trigger)) {
throw new Error('is not safe to use in this trigger');
@ -197,12 +205,20 @@ const PlayerOps: CommandHandlers = {
state.pushInt(state.activePlayer.lastItem);
},
[ScriptOpcode.LAST_SLOT]: (state) => {
[ScriptOpcode.LAST_SLOT]: state => {
const allowedTriggers = [
ServerTriggerType.OPHELD1, ServerTriggerType.OPHELD2, ServerTriggerType.OPHELD3, ServerTriggerType.OPHELD4, ServerTriggerType.OPHELD5,
ServerTriggerType.OPHELD1,
ServerTriggerType.OPHELD2,
ServerTriggerType.OPHELD3,
ServerTriggerType.OPHELD4,
ServerTriggerType.OPHELD5,
ServerTriggerType.OPHELDU,
ServerTriggerType.OPHELDT,
ServerTriggerType.INV_BUTTON1, ServerTriggerType.INV_BUTTON2, ServerTriggerType.INV_BUTTON3, ServerTriggerType.INV_BUTTON4, ServerTriggerType.INV_BUTTON5,
ServerTriggerType.INV_BUTTON1,
ServerTriggerType.INV_BUTTON2,
ServerTriggerType.INV_BUTTON3,
ServerTriggerType.INV_BUTTON4,
ServerTriggerType.INV_BUTTON5,
ServerTriggerType.INV_BUTTOND
];
if (!allowedTriggers.includes(state.trigger)) {
@ -212,11 +228,17 @@ const PlayerOps: CommandHandlers = {
state.pushInt(state.activePlayer.lastSlot);
},
[ScriptOpcode.LAST_USEITEM]: (state) => {
[ScriptOpcode.LAST_USEITEM]: state => {
const allowedTriggers = [
ServerTriggerType.OPHELDU,
ServerTriggerType.APOBJU, ServerTriggerType.APLOCU, ServerTriggerType.APNPCU, ServerTriggerType.APPLAYERU,
ServerTriggerType.OPOBJU, ServerTriggerType.OPLOCU, ServerTriggerType.OPNPCU, ServerTriggerType.OPPLAYERU,
ServerTriggerType.APOBJU,
ServerTriggerType.APLOCU,
ServerTriggerType.APNPCU,
ServerTriggerType.APPLAYERU,
ServerTriggerType.OPOBJU,
ServerTriggerType.OPLOCU,
ServerTriggerType.OPNPCU,
ServerTriggerType.OPPLAYERU
];
if (!allowedTriggers.includes(state.trigger)) {
throw new Error('is not safe to use in this trigger');
@ -225,11 +247,17 @@ const PlayerOps: CommandHandlers = {
state.pushInt(state.activePlayer.lastUseItem);
},
[ScriptOpcode.LAST_USESLOT]: (state) => {
[ScriptOpcode.LAST_USESLOT]: state => {
const allowedTriggers = [
ServerTriggerType.OPHELDU,
ServerTriggerType.APOBJU, ServerTriggerType.APLOCU, ServerTriggerType.APNPCU, ServerTriggerType.APPLAYERU,
ServerTriggerType.OPOBJU, ServerTriggerType.OPLOCU, ServerTriggerType.OPNPCU, ServerTriggerType.OPPLAYERU,
ServerTriggerType.APOBJU,
ServerTriggerType.APLOCU,
ServerTriggerType.APNPCU,
ServerTriggerType.APPLAYERU,
ServerTriggerType.OPOBJU,
ServerTriggerType.OPLOCU,
ServerTriggerType.OPNPCU,
ServerTriggerType.OPPLAYERU
];
if (!allowedTriggers.includes(state.trigger)) {
throw new Error('is not safe to use in this trigger');
@ -238,7 +266,7 @@ const PlayerOps: CommandHandlers = {
state.pushInt(state.activePlayer.lastUseSlot);
},
[ScriptOpcode.MES]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.MES]: checkedHandler(ActivePlayer, state => {
const message = state.popString();
if (Environment.CLIRUNNER) {
@ -248,11 +276,11 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.messageGame(message);
}),
[ScriptOpcode.NAME]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.NAME]: checkedHandler(ActivePlayer, state => {
state.pushString(state.activePlayer.username);
}),
[ScriptOpcode.P_APRANGE]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_APRANGE]: checkedHandler(ProtectedActivePlayer, state => {
const apRange = state.popInt();
if (apRange === -1) {
throw new Error('attempted to use a range that was null.');
@ -262,7 +290,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.apRangeCalled = true;
}),
[ScriptOpcode.P_ARRIVEDELAY]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_ARRIVEDELAY]: checkedHandler(ProtectedActivePlayer, state => {
if (state.activePlayer.lastMovement < World.currentTick) {
return;
}
@ -271,22 +299,22 @@ const PlayerOps: CommandHandlers = {
state.execution = ScriptState.SUSPENDED;
}),
[ScriptOpcode.P_COUNTDIALOG]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_COUNTDIALOG]: checkedHandler(ProtectedActivePlayer, state => {
state.activePlayer.write(ServerProt.P_COUNTDIALOG);
state.execution = ScriptState.COUNTDIALOG;
}),
[ScriptOpcode.P_DELAY]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_DELAY]: checkedHandler(ProtectedActivePlayer, state => {
state.activePlayer.delay = state.popInt() + 1;
state.execution = ScriptState.SUSPENDED;
// TODO should this wipe any pointers?
}),
[ScriptOpcode.P_OPHELD]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_OPHELD]: checkedHandler(ProtectedActivePlayer, state => {
throw new Error('unimplemented');
}),
[ScriptOpcode.P_OPLOC]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_OPLOC]: checkedHandler(ProtectedActivePlayer, state => {
const type = state.popInt() - 1;
if (type < 0 || type >= 5) {
throw new Error(`Invalid oploc: ${type + 1}`);
@ -297,7 +325,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.setInteraction(state.activeLoc, ServerTriggerType.APLOC1 + type);
}),
[ScriptOpcode.P_OPNPC]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_OPNPC]: checkedHandler(ProtectedActivePlayer, state => {
const type = state.popInt() - 1;
if (type < 0 || type >= 5) {
throw new Error(`Invalid opnpc: ${type + 1}`);
@ -311,12 +339,12 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.setInteraction(state.activeNpc, ServerTriggerType.APNPC1 + type);
}),
[ScriptOpcode.P_PAUSEBUTTON]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_PAUSEBUTTON]: checkedHandler(ProtectedActivePlayer, state => {
state.execution = ScriptState.PAUSEBUTTON;
// TODO last_com
}),
[ScriptOpcode.P_STOPACTION]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_STOPACTION]: checkedHandler(ProtectedActivePlayer, state => {
// clear current walk queue, clear current interaction, close interface, clear suspended script? > not the script, cant emote while going thru toll
state.activePlayer.clearInteraction();
state.activePlayer.closeModal();
@ -324,7 +352,7 @@ const PlayerOps: CommandHandlers = {
// state.activePlayer.activeScript = null;
}),
[ScriptOpcode.P_TELEJUMP]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_TELEJUMP]: checkedHandler(ProtectedActivePlayer, state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -336,7 +364,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.teleJump(pos.x, pos.z, pos.level);
}),
[ScriptOpcode.P_TELEPORT]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_TELEPORT]: checkedHandler(ProtectedActivePlayer, state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -348,7 +376,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.teleport(pos.x, pos.z, pos.level);
}),
[ScriptOpcode.P_WALK]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_WALK]: checkedHandler(ProtectedActivePlayer, state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -362,33 +390,33 @@ const PlayerOps: CommandHandlers = {
player.updateMovement(); // try to walk immediately
}),
[ScriptOpcode.SAY]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.SAY]: checkedHandler(ActivePlayer, state => {
state.activePlayer.say(state.popString());
}),
[ScriptOpcode.SOUND_SYNTH]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.SOUND_SYNTH]: checkedHandler(ActivePlayer, state => {
const [synth, loops, delay] = state.popInts(3);
state.activePlayer.write(ServerProt.SYNTH_SOUND, synth, loops, delay);
}),
[ScriptOpcode.STAFFMODLEVEL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.STAFFMODLEVEL]: checkedHandler(ActivePlayer, state => {
throw new Error('unimplemented');
}),
[ScriptOpcode.STAT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.STAT]: checkedHandler(ActivePlayer, state => {
const stat = state.popInt();
state.pushInt(state.activePlayer.levels[stat]);
}),
[ScriptOpcode.STAT_BASE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.STAT_BASE]: checkedHandler(ActivePlayer, state => {
const stat = state.popInt();
state.pushInt(state.activePlayer.baseLevels[stat]);
}),
[ScriptOpcode.STAT_ADD]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.STAT_ADD]: checkedHandler(ActivePlayer, state => {
const [stat, constant, percent] = state.popInts(3);
const player = state.activePlayer;
@ -397,7 +425,7 @@ const PlayerOps: CommandHandlers = {
player.levels[stat] = Math.min(added, 255);
}),
[ScriptOpcode.STAT_SUB]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.STAT_SUB]: checkedHandler(ActivePlayer, state => {
const [stat, constant, percent] = state.popInts(3);
const player = state.activePlayer;
@ -406,7 +434,7 @@ const PlayerOps: CommandHandlers = {
player.levels[stat] = Math.max(subbed, 0);
}),
[ScriptOpcode.SPOTANIM_PL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.SPOTANIM_PL]: checkedHandler(ActivePlayer, state => {
const delay = state.popInt();
const height = state.popInt();
const spotanim = state.popInt();
@ -414,7 +442,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.spotanim(spotanim, height, delay);
}),
[ScriptOpcode.STAT_HEAL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.STAT_HEAL]: checkedHandler(ActivePlayer, state => {
const [stat, constant, percent] = state.popInts(3);
const player = state.activePlayer;
@ -424,137 +452,137 @@ const PlayerOps: CommandHandlers = {
player.levels[stat] = Math.min(healed, base);
}),
[ScriptOpcode.UID]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.UID]: checkedHandler(ActivePlayer, state => {
state.pushInt(state.activePlayer.uid);
}),
[ScriptOpcode.P_LOGOUT]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_LOGOUT]: checkedHandler(ProtectedActivePlayer, state => {
state.activePlayer.logoutRequested = true;
}),
[ScriptOpcode.IF_SETCOLOUR]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETCOLOUR]: checkedHandler(ActivePlayer, state => {
const colour = state.popInt();
const com = state.popInt();
state.activePlayer.write(ServerProt.IF_SETCOLOUR, com, colour);
}),
[ScriptOpcode.IF_OPENCHAT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_OPENCHAT]: checkedHandler(ActivePlayer, state => {
const com = state.popInt();
state.activePlayer.openChat(com);
}),
[ScriptOpcode.IF_OPENMAINMODALSIDEOVERLAY]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_OPENMAINMODALSIDEOVERLAY]: checkedHandler(ActivePlayer, state => {
const [main, side] = state.popInts(2);
state.activePlayer.openMainModalSideOverlay(main, side);
}),
[ScriptOpcode.IF_SETHIDE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETHIDE]: checkedHandler(ActivePlayer, state => {
const [com, hide] = state.popInts(2);
state.activePlayer.write(ServerProt.IF_SETHIDE, com, hide === 1);
}),
[ScriptOpcode.IF_SETOBJECT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETOBJECT]: checkedHandler(ActivePlayer, state => {
const [com, obj, scale] = state.popInts(3);
state.activePlayer.write(ServerProt.IF_SETOBJECT, com, obj, scale);
}),
[ScriptOpcode.IF_SETTABACTIVE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETTABACTIVE]: checkedHandler(ActivePlayer, state => {
const tab = state.popInt();
state.activePlayer.write(ServerProt.IF_SHOWSIDE, tab);
}),
[ScriptOpcode.IF_SETMODEL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETMODEL]: checkedHandler(ActivePlayer, state => {
const [com, model] = state.popInts(2);
state.activePlayer.write(ServerProt.IF_SETMODEL, com, model);
}),
[ScriptOpcode.IF_SETRECOL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETRECOL]: checkedHandler(ActivePlayer, state => {
const [com, src, dest] = state.popInts(3);
state.activePlayer.write(ServerProt.IF_SETRECOL, com, src, dest);
}),
[ScriptOpcode.IF_SETTABFLASH]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETTABFLASH]: checkedHandler(ActivePlayer, state => {
const tab = state.popInt();
state.activePlayer.write(ServerProt.TUTORIAL_FLASHSIDE, tab);
}),
[ScriptOpcode.IF_SETANIM]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETANIM]: checkedHandler(ActivePlayer, state => {
const [com, seq] = state.popInts(2);
state.activePlayer.write(ServerProt.IF_SETANIM, com, seq);
}),
[ScriptOpcode.IF_SETTAB]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETTAB]: checkedHandler(ActivePlayer, state => {
const [com, tab] = state.popInts(2);
state.activePlayer.setTab(com, tab);
}),
[ScriptOpcode.IF_OPENMAINMODAL]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_OPENMAINMODAL]: checkedHandler(ActivePlayer, state => {
const com = state.popInt();
state.activePlayer.openMainModal(com);
}),
[ScriptOpcode.IF_OPENCHATSTICKY]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_OPENCHATSTICKY]: checkedHandler(ActivePlayer, state => {
const com = state.popInt();
state.activePlayer.openChatSticky(com);
}),
[ScriptOpcode.IF_OPENSIDEOVERLAY]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_OPENSIDEOVERLAY]: checkedHandler(ActivePlayer, state => {
const com = state.popInt();
state.activePlayer.openSideOverlay(com);
}),
[ScriptOpcode.IF_SETPLAYERHEAD]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETPLAYERHEAD]: checkedHandler(ActivePlayer, state => {
const com = state.popInt();
state.activePlayer.write(ServerProt.IF_SETPLAYERHEAD, com);
}),
[ScriptOpcode.IF_SETTEXT]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETTEXT]: checkedHandler(ActivePlayer, state => {
const text = state.popString();
const com = state.popInt();
state.activePlayer.write(ServerProt.IF_SETTEXT, com, text);
}),
[ScriptOpcode.IF_SETNPCHEAD]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETNPCHEAD]: checkedHandler(ActivePlayer, state => {
const [com, npc] = state.popInts(2);
state.activePlayer.write(ServerProt.IF_SETNPCHEAD, com, npc);
}),
[ScriptOpcode.IF_SETPOSITION]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETPOSITION]: checkedHandler(ActivePlayer, state => {
const [com, x, y] = state.popInts(3);
state.activePlayer.write(ServerProt.IF_SETPOSITION, com, x, y);
}),
[ScriptOpcode.IF_MULTIZONE]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_MULTIZONE]: checkedHandler(ActivePlayer, state => {
const multi = state.popInt();
state.activePlayer.write(ServerProt.SET_MULTIWAY, multi === 1);
}),
[ScriptOpcode.GIVEXP]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.GIVEXP]: checkedHandler(ProtectedActivePlayer, state => {
const [stat, xp] = state.popInts(2);
state.activePlayer.addXp(stat, xp);
}),
[ScriptOpcode.DAMAGE]: (state) => {
[ScriptOpcode.DAMAGE]: state => {
const amount = state.popInt();
const type = state.popInt();
const uid = state.popInt();
@ -567,13 +595,13 @@ const PlayerOps: CommandHandlers = {
player.applyDamage(amount, type);
},
[ScriptOpcode.IF_SETRESUMEBUTTONS]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.IF_SETRESUMEBUTTONS]: checkedHandler(ActivePlayer, state => {
const [button1, button2, button3, button4, button5] = state.popInts(5);
state.activePlayer.resumeButtons = [button1, button2, button3, button4, button5];
}),
[ScriptOpcode.TEXT_GENDER]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.TEXT_GENDER]: checkedHandler(ActivePlayer, state => {
const [male, female] = state.popStrings(2);
if (state.activePlayer.gender == 0) {
state.pushString(male);
@ -582,17 +610,17 @@ const PlayerOps: CommandHandlers = {
}
}),
[ScriptOpcode.MIDI_SONG]: (state) => {
[ScriptOpcode.MIDI_SONG]: state => {
state.activePlayer.playSong(state.popString());
},
[ScriptOpcode.MIDI_JINGLE]: (state) => {
[ScriptOpcode.MIDI_JINGLE]: state => {
const delay = state.popInt();
const name = state.popString();
state.activePlayer.playJingle(delay, name);
},
[ScriptOpcode.SOFTTIMER]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.SOFTTIMER]: checkedHandler(ActivePlayer, state => {
const args = popScriptArgs(state);
const interval = state.popInt();
const timerId = state.popInt();
@ -604,13 +632,13 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.setTimer(PlayerTimerType.SOFT, script, args, interval);
}),
[ScriptOpcode.CLEARSOFTTIMER]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.CLEARSOFTTIMER]: checkedHandler(ActivePlayer, state => {
const timerId = state.popInt();
state.activePlayer.clearTimer(timerId);
}),
[ScriptOpcode.SETTIMER]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.SETTIMER]: checkedHandler(ActivePlayer, state => {
const args = popScriptArgs(state);
const interval = state.popInt();
const timerId = state.popInt();
@ -622,13 +650,13 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.setTimer(PlayerTimerType.NORMAL, script, args, interval);
}),
[ScriptOpcode.CLEARTIMER]: checkedHandler(ActivePlayer, (state) => {
[ScriptOpcode.CLEARTIMER]: checkedHandler(ActivePlayer, state => {
const timerId = state.popInt();
state.activePlayer.clearTimer(timerId);
}),
[ScriptOpcode.HINT_COORD]: (state) => {
[ScriptOpcode.HINT_COORD]: state => {
const [offset, coord, height] = state.popInts(3);
if (coord < 0 || coord > Position.max) {
@ -640,15 +668,15 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.hintTile(offset, pos.x, pos.z, height);
},
[ScriptOpcode.HINT_STOP]: (state) => {
[ScriptOpcode.HINT_STOP]: state => {
state.activePlayer.stopHint();
},
[ScriptOpcode.IF_CLOSESTICKY]: (state) => {
[ScriptOpcode.IF_CLOSESTICKY]: state => {
state.activePlayer.closeSticky();
},
[ScriptOpcode.P_EXACTMOVE]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_EXACTMOVE]: checkedHandler(ProtectedActivePlayer, state => {
const [start, end, startCycle, endCycle, direction] = state.popInts(5);
const startPos = Position.unpackCoord(start);
@ -658,11 +686,11 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.exactMove(startPos.x, startPos.z, endPos.x, endPos.z, startCycle, endCycle, direction);
}),
[ScriptOpcode.BUSY]: (state) => {
[ScriptOpcode.BUSY]: state => {
state.pushInt(state.activePlayer.busy() ? 1 : 0);
},
[ScriptOpcode.GETQUEUE]: (state) => {
[ScriptOpcode.GETQUEUE]: state => {
const scriptId = state.popInt();
const queue = state.activePlayer.queue.filter(req => req.script.id === scriptId).length;
@ -671,7 +699,7 @@ const PlayerOps: CommandHandlers = {
},
// TODO: check active loc too
[ScriptOpcode.P_LOCMERGE]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_LOCMERGE]: checkedHandler(ProtectedActivePlayer, state => {
const [startCycle, endCycle, southEast, northWest] = state.popInts(4);
const se = Position.unpackCoord(southEast);
@ -687,7 +715,7 @@ const PlayerOps: CommandHandlers = {
World.getZone(loc.x, loc.z, loc.level).mergeLoc(loc, state.activePlayer, startCycle, endCycle, south, east, north, west);
}),
[ScriptOpcode.LAST_LOGIN_INFO]: (state) => {
[ScriptOpcode.LAST_LOGIN_INFO]: state => {
const player = state.activePlayer;
const client = player.client;
if (client == null) {
@ -706,59 +734,59 @@ const PlayerOps: CommandHandlers = {
player.lastLoginInfo(lastLoginIp, 0, 201, 0);
},
[ScriptOpcode.BAS_READYANIM]: (state) => {
[ScriptOpcode.BAS_READYANIM]: state => {
state.activePlayer.basReadyAnim = state.popInt();
},
[ScriptOpcode.BAS_TURNONSPOT]: (state) => {
[ScriptOpcode.BAS_TURNONSPOT]: state => {
state.activePlayer.basTurnOnSpot = state.popInt();
},
[ScriptOpcode.BAS_WALK_F]: (state) => {
[ScriptOpcode.BAS_WALK_F]: state => {
state.activePlayer.basWalkForward = state.popInt();
},
[ScriptOpcode.BAS_WALK_B]: (state) => {
[ScriptOpcode.BAS_WALK_B]: state => {
state.activePlayer.basWalkBackward = state.popInt();
},
[ScriptOpcode.BAS_WALK_L]: (state) => {
[ScriptOpcode.BAS_WALK_L]: state => {
state.activePlayer.basWalkLeft = state.popInt();
},
[ScriptOpcode.BAS_WALK_R]: (state) => {
[ScriptOpcode.BAS_WALK_R]: state => {
state.activePlayer.basWalkRight = state.popInt();
},
[ScriptOpcode.BAS_RUNNING]: (state) => {
[ScriptOpcode.BAS_RUNNING]: state => {
state.activePlayer.basRunning = state.popInt();
},
[ScriptOpcode.GENDER]: (state) => {
[ScriptOpcode.GENDER]: state => {
state.pushInt(state.activePlayer.gender);
},
[ScriptOpcode.HINT_NPC]: (state) => {
[ScriptOpcode.HINT_NPC]: state => {
const npc_uid = state.popInt();
state.activePlayer.hintNpc(npc_uid);
},
[ScriptOpcode.HINT_PLAYER]: (state) => {
[ScriptOpcode.HINT_PLAYER]: state => {
const player_uid = state.popInt();
state.activePlayer.hintPlayer(player_uid);
},
[ScriptOpcode.HEADICONS_GET]: (state) => {
[ScriptOpcode.HEADICONS_GET]: state => {
state.pushInt(state.activePlayer.headicons);
},
[ScriptOpcode.HEADICONS_SET]: (state) => {
[ScriptOpcode.HEADICONS_SET]: state => {
state.activePlayer.headicons = state.popInt();
},
[ScriptOpcode.P_OPOBJ]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_OPOBJ]: checkedHandler(ProtectedActivePlayer, state => {
const type = state.popInt() - 1;
if (type < 0 || type >= 5) {
throw new Error(`Invalid opobj: ${type + 1}`);
@ -769,7 +797,7 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.setInteraction(state.activeObj, ServerTriggerType.APOBJ1 + type);
}),
[ScriptOpcode.P_OPPLAYER]: checkedHandler(ProtectedActivePlayer, (state) => {
[ScriptOpcode.P_OPPLAYER]: checkedHandler(ProtectedActivePlayer, state => {
const type = state.popInt() - 1;
if (type < 0 || type >= 5) {
throw new Error(`Invalid opplayer: ${type + 1}`);
@ -787,16 +815,14 @@ const PlayerOps: CommandHandlers = {
state.activePlayer.setInteraction(target, ServerTriggerType.APPLAYER1 + type);
}),
[ScriptOpcode.ALLOWDESIGN]: (state) => {
[ScriptOpcode.ALLOWDESIGN]: state => {
const allow = state.popInt();
state.activePlayer.allowDesign = allow === 1;
},
[ScriptOpcode.LAST_TARGETSLOT]: (state) => {
const allowedTriggers = [
ServerTriggerType.INV_BUTTOND
];
[ScriptOpcode.LAST_TARGETSLOT]: state => {
const allowedTriggers = [ServerTriggerType.INV_BUTTOND];
if (!allowedTriggers.includes(state.trigger)) {
throw new Error('is not safe to use in this trigger');
}
@ -804,18 +830,18 @@ const PlayerOps: CommandHandlers = {
state.pushInt(state.activePlayer.lastTargetSlot);
},
[ScriptOpcode.SETMOVECHECK]: (state) => {
[ScriptOpcode.SETMOVECHECK]: state => {
state.activePlayer.moveCheck = state.popInt();
},
[ScriptOpcode.CLEARQUEUE]: (state) => {
[ScriptOpcode.CLEARQUEUE]: state => {
const scriptId = state.popInt();
state.activePlayer.queue = state.activePlayer.queue.filter(req => req.script.id !== scriptId);
state.activePlayer.weakQueue = state.activePlayer.weakQueue.filter(req => req.script.id !== scriptId);
},
[ScriptOpcode.HEALENERGY]: (state) => {
[ScriptOpcode.HEALENERGY]: state => {
const amount = state.popInt(); // 100=1%, 1000=10%, 10000=100%
const player = state.activePlayer;
@ -823,10 +849,10 @@ const PlayerOps: CommandHandlers = {
player.runenergy = energy;
},
[ScriptOpcode.AFK_EVENT]: (state) => {
[ScriptOpcode.AFK_EVENT]: state => {
state.pushInt(state.activePlayer.afkEventReady ? 1 : 0);
state.activePlayer.afkEventReady = false;
},
}
};
/**

View file

@ -21,27 +21,27 @@ import { Position } from '#lostcity/entity/Position.js';
import { CollisionFlag } from '@2004scape/rsmod-pathfinder';
const ServerOps: CommandHandlers = {
[ScriptOpcode.MAP_CLOCK]: (state) => {
[ScriptOpcode.MAP_CLOCK]: state => {
state.pushInt(World.currentTick);
},
[ScriptOpcode.MAP_MEMBERS]: (state) => {
[ScriptOpcode.MAP_MEMBERS]: state => {
state.pushInt(World.members ? 1 : 0);
},
[ScriptOpcode.MAP_PLAYERCOUNT]: (state) => {
[ScriptOpcode.MAP_PLAYERCOUNT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.HUNTALL]: (state) => {
[ScriptOpcode.HUNTALL]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.HUNTNEXT]: (state) => {
[ScriptOpcode.HUNTNEXT]: state => {
throw new Error('unimplemented');
},
[ScriptOpcode.INZONE]: (state) => {
[ScriptOpcode.INZONE]: state => {
const [c1, c2, c3] = state.popInts(3);
if (c1 < 0 || c1 > Position.max) {
@ -67,7 +67,7 @@ const ServerOps: CommandHandlers = {
}
},
[ScriptOpcode.LINEOFWALK]: (state) => {
[ScriptOpcode.LINEOFWALK]: state => {
const [c1, c2] = state.popInts(2);
if (c1 < 0 || c1 > Position.max) {
@ -82,16 +82,16 @@ const ServerOps: CommandHandlers = {
state.pushInt(World.lineValidator.hasLineOfWalk(from.level, from.x, from.z, to.x, to.z, 1, 1, 1) ? 1 : 0);
},
[ScriptOpcode.STAT_RANDOM]: (state) => {
[ScriptOpcode.STAT_RANDOM]: state => {
const [level, low, high] = state.popInts(3);
const value = Math.floor(low * (99 - level) / 98) + Math.floor(high * (level - 1) / 98) + 1;
const value = Math.floor((low * (99 - level)) / 98) + Math.floor((high * (level - 1)) / 98) + 1;
const chance = Math.floor(Math.random() * 256);
state.pushInt(value > chance ? 1 : 0);
},
[ScriptOpcode.SPOTANIM_MAP]: (state) => {
[ScriptOpcode.SPOTANIM_MAP]: state => {
const [spotanim, coord, height, delay] = state.popInts(4);
if (coord < 0 || coord > Position.max) {
@ -106,7 +106,7 @@ const ServerOps: CommandHandlers = {
World.getZone(x, z, level).animMap(x, z, spotanim, height, delay);
},
[ScriptOpcode.DISTANCE]: (state) => {
[ScriptOpcode.DISTANCE]: state => {
const [c1, c2] = state.popInts(2);
if (c1 < 0 || c1 > Position.max) {
@ -124,7 +124,7 @@ const ServerOps: CommandHandlers = {
state.pushInt(Math.max(dx, dz));
},
[ScriptOpcode.MOVECOORD]: (state) => {
[ScriptOpcode.MOVECOORD]: state => {
const [coord, x, y, z] = state.popInts(4);
if (coord < 0 || coord > Position.max) {
@ -135,13 +135,13 @@ const ServerOps: CommandHandlers = {
state.pushInt(Position.packCoord(pos.level + y, pos.x + x, pos.z + z));
},
[ScriptOpcode.SEQLENGTH]: (state) => {
[ScriptOpcode.SEQLENGTH]: state => {
const seq = state.popInt();
state.pushInt(SeqType.get(seq).duration);
},
[ScriptOpcode.SPLIT_INIT]: (state) => {
[ScriptOpcode.SPLIT_INIT]: state => {
const [maxWidth, linesPerPage, fontId, mesanimId] = state.popInts(4);
const text = state.popString();
const font = FontType.get(fontId);
@ -154,23 +154,23 @@ const ServerOps: CommandHandlers = {
}
},
[ScriptOpcode.SPLIT_GET]: (state) => {
[ScriptOpcode.SPLIT_GET]: state => {
const [page, line] = state.popInts(2);
state.pushString(state.splitPages[page][line]);
},
[ScriptOpcode.SPLIT_PAGECOUNT]: (state) => {
[ScriptOpcode.SPLIT_PAGECOUNT]: state => {
state.pushInt(state.splitPages.length);
},
[ScriptOpcode.SPLIT_LINECOUNT]: (state) => {
[ScriptOpcode.SPLIT_LINECOUNT]: state => {
const page = state.popInt();
state.pushInt(state.splitPages[page].length);
},
[ScriptOpcode.SPLIT_GETANIM]: (state) => {
[ScriptOpcode.SPLIT_GETANIM]: state => {
const page = state.popInt();
if (state.splitMesanim === -1) {
state.pushInt(-1);
@ -181,7 +181,7 @@ const ServerOps: CommandHandlers = {
state.pushInt(mesanimType.len[state.splitPages[page].length - 1]);
},
[ScriptOpcode.STRUCT_PARAM]: (state) => {
[ScriptOpcode.STRUCT_PARAM]: state => {
const [structId, paramId] = state.popInts(2);
const param = ParamType.get(paramId);
const struct = StructType.get(structId);
@ -192,7 +192,7 @@ const ServerOps: CommandHandlers = {
}
},
[ScriptOpcode.COORDX]: (state) => {
[ScriptOpcode.COORDX]: state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -202,7 +202,7 @@ const ServerOps: CommandHandlers = {
state.pushInt((coord >> 14) & 0x3fff);
},
[ScriptOpcode.COORDY]: (state) => {
[ScriptOpcode.COORDY]: state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -212,7 +212,7 @@ const ServerOps: CommandHandlers = {
state.pushInt((coord >> 28) & 0x3);
},
[ScriptOpcode.COORDZ]: (state) => {
[ScriptOpcode.COORDZ]: state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -222,11 +222,11 @@ const ServerOps: CommandHandlers = {
state.pushInt(coord & 0x3fff);
},
[ScriptOpcode.PLAYERCOUNT]: (state) => {
[ScriptOpcode.PLAYERCOUNT]: state => {
state.pushInt(World.getTotalPlayers());
},
[ScriptOpcode.MAP_BLOCKED]: (state) => {
[ScriptOpcode.MAP_BLOCKED]: state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -237,7 +237,7 @@ const ServerOps: CommandHandlers = {
state.pushInt(World.collisionFlags.isFlagged(pos.x, pos.z, pos.level, CollisionFlag.WALK_BLOCKED) ? 1 : 0);
},
[ScriptOpcode.LINEOFSIGHT]: (state) => {
[ScriptOpcode.LINEOFSIGHT]: state => {
const [c1, c2] = state.popInts(2);
if (c1 < 0 || c1 > Position.max) {
@ -252,12 +252,12 @@ const ServerOps: CommandHandlers = {
state.pushInt(World.lineValidator.hasLineOfSight(from.level, from.x, from.z, to.x, to.z, 1, 1, 1) ? 1 : 0);
},
[ScriptOpcode.WORLD_DELAY]: (state) => {
[ScriptOpcode.WORLD_DELAY]: state => {
// arg is popped elsewhere
state.execution = ScriptState.WORLD_SUSPENDED;
},
[ScriptOpcode.PROJANIM_PL]: (state) => {
[ScriptOpcode.PROJANIM_PL]: state => {
const [srcCoord, uid, spotanim, srcHeight, dstHeight, delay, duration, peak, arc] = state.popInts(9);
if (srcCoord < 0 || srcCoord > Position.max) {
@ -274,15 +274,15 @@ const ServerOps: CommandHandlers = {
zone.mapProjAnim(srcPos.x, srcPos.z, player.x, player.z, -player.pid - 1, spotanim, srcHeight + 100, dstHeight + 100, delay, duration, peak, arc);
},
[ScriptOpcode.PROJANIM_NPC]: (state) => {
[ScriptOpcode.PROJANIM_NPC]: state => {
const [srcCoord, npcUid, spotanim, srcHeight, dstHeight, delay, duration, peak, arc] = state.popInts(9);
if (srcCoord < 0 || srcCoord > Position.max) {
throw new Error(`attempted to use coord that was out of range: ${srcCoord}. Range should be: 0 to ${Position.max}`);
}
const slot = npcUid & 0xFFFF;
const expectedType = npcUid >> 16 & 0xFFFF;
const slot = npcUid & 0xffff;
const expectedType = (npcUid >> 16) & 0xffff;
const npc = World.getNpc(slot);
if (!npc) {
@ -294,7 +294,7 @@ const ServerOps: CommandHandlers = {
zone.mapProjAnim(srcPos.x, srcPos.z, npc.x, npc.z, npc.nid + 1, spotanim, srcHeight + 100, dstHeight + 100, delay, duration, peak, arc);
},
[ScriptOpcode.PROJANIM_MAP]: (state) => {
[ScriptOpcode.PROJANIM_MAP]: state => {
const [srcCoord, dstCoord, spotanim, srcHeight, dstHeight, delay, duration, peak, arc] = state.popInts(9);
if (srcCoord < 0 || srcCoord > Position.max) {
@ -309,7 +309,7 @@ const ServerOps: CommandHandlers = {
zone.mapProjAnim(srcPos.x, srcPos.z, dstPos.x, dstPos.z, 0, spotanim, srcHeight + 100, dstHeight, delay, duration, peak, arc);
},
[ScriptOpcode.MAP_LOCADDUNSAFE]: (state) => {
[ScriptOpcode.MAP_LOCADDUNSAFE]: state => {
const coord = state.popInt();
if (coord < 0 || coord > Position.max) {
@ -341,11 +341,11 @@ const ServerOps: CommandHandlers = {
return;
}
} else if (layer === LocLayer.GROUND) {
const width = (loc.angle === LocAngle.NORTH || loc.angle === LocAngle.SOUTH) ? loc.length : loc.width;
const length = (loc.angle === LocAngle.NORTH || loc.angle === LocAngle.SOUTH) ? loc.width : loc.length;
const width = loc.angle === LocAngle.NORTH || loc.angle === LocAngle.SOUTH ? loc.length : loc.width;
const length = loc.angle === LocAngle.NORTH || loc.angle === LocAngle.SOUTH ? loc.width : loc.length;
for (let index = 0; index < width * length; index++) {
const deltaX = loc.x + (index % width);
const deltaZ = loc.z + (index / width);
const deltaZ = loc.z + index / width;
if (deltaX === pos.x && deltaZ === pos.z) {
state.pushInt(1);
return;
@ -359,7 +359,7 @@ const ServerOps: CommandHandlers = {
}
}
state.pushInt(0);
},
}
};
export default ServerOps;
export default ServerOps;

View file

@ -2,18 +2,18 @@ import { CommandHandlers } from '#lostcity/engine/script/ScriptRunner.js';
import ScriptOpcode from '#lostcity/engine/script/ScriptOpcode.js';
const StringOps: CommandHandlers = {
[ScriptOpcode.APPEND_NUM]: (state) => {
[ScriptOpcode.APPEND_NUM]: state => {
const text = state.popString();
const num = state.popInt();
state.pushString(text + num);
},
[ScriptOpcode.APPEND]: (state) => {
[ScriptOpcode.APPEND]: state => {
const [t1, t2] = state.popStrings(2);
state.pushString(t1 + t2);
},
[ScriptOpcode.APPEND_SIGNNUM]: (state) => {
[ScriptOpcode.APPEND_SIGNNUM]: state => {
const text = state.popString();
const num = state.popInt();
@ -24,52 +24,52 @@ const StringOps: CommandHandlers = {
}
},
[ScriptOpcode.LOWERCASE]: (state) => {
[ScriptOpcode.LOWERCASE]: state => {
state.pushString(state.popString().toLowerCase());
},
[ScriptOpcode.TOSTRING]: (state) => {
[ScriptOpcode.TOSTRING]: state => {
state.pushString(state.popInt().toString());
},
[ScriptOpcode.COMPARE]: (state) => {
[ScriptOpcode.COMPARE]: state => {
const [s1, s2] = state.popStrings(2);
state.pushInt(javaStringCompare(s1, s2));
},
[ScriptOpcode.TEXT_SWITCH]: (state) => {
[ScriptOpcode.TEXT_SWITCH]: state => {
const value = state.popInt();
const [s1, s2] = state.popStrings(2);
state.pushString(value === 1 ? s1 : s2);
},
[ScriptOpcode.APPEND_CHAR]: (state) => {
[ScriptOpcode.APPEND_CHAR]: state => {
const text = state.popString();
const char = state.popInt();
state.pushString(text + String.fromCharCode(char));
},
[ScriptOpcode.STRING_LENGTH]: (state) => {
[ScriptOpcode.STRING_LENGTH]: state => {
state.pushInt(state.popString().length);
},
[ScriptOpcode.SUBSTRING]: (state) => {
[ScriptOpcode.SUBSTRING]: state => {
const text = state.popString();
const [start, end] = state.popInts(2);
state.pushString(text.substring(start, end));
},
[ScriptOpcode.STRING_INDEXOF_CHAR]: (state) => {
[ScriptOpcode.STRING_INDEXOF_CHAR]: state => {
const text = state.popString();
const find = String.fromCharCode(state.popInt());
state.pushInt(text.indexOf(find));
},
[ScriptOpcode.STRING_INDEXOF_STRING]: (state) => {
[ScriptOpcode.STRING_INDEXOF_STRING]: state => {
const text = state.popString();
const find = state.popString();
state.pushInt(text.indexOf(find));
},
}
};
function javaStringCompare(a: string, b: string): number {

View file

@ -42,13 +42,7 @@ export default class Zone {
// variables fully broken out for now
//coord $from, coord $to, spotanim $spotanim, int $fromHeight, int $toHeight, int $startDelay, int $endDelay, int $peak, int $arc
static mapProjAnim(srcX: number, srcZ: number,
dstX: number, dstZ: number,
target: number, spotanim: number,
srcHeight: number, dstHeight: number,
startDelay: number, endDelay: number,
peak: number, arc: number)
{
static mapProjAnim(srcX: number, srcZ: number, dstX: number, dstZ: number, target: number, spotanim: number, srcHeight: number, dstHeight: number, startDelay: number, endDelay: number, peak: number, arc: number) {
const out = new Packet();
out.p1(ServerProt.MAP_PROJANIM);
@ -90,11 +84,7 @@ export default class Zone {
// merge player with loc, e.g. agility training through pipes
// useful due to draw prioritizes
static locMerge(srcX: number, srcZ: number,
shape: number, angle: number, locId: number,
startCycle: number, endCycle: number,
pid: number, east: number, south: number, west: number, north: number)
{
static locMerge(srcX: number, srcZ: number, shape: number, angle: number, locId: number, startCycle: number, endCycle: number, pid: number, east: number, south: number, west: number, north: number) {
const out = new Packet();
out.p1(ServerProt.LOC_MERGE);
@ -224,13 +214,7 @@ export default class Zone {
this.lastEvent = World.currentTick;
}
mapProjAnim(x: number, z: number,
dstX: number, dstZ: number,
target: number, spotanim: number,
srcHeight: number, dstHeight: number,
startDelay: number, endDelay: number,
peak: number, arc: number)
{
mapProjAnim(x: number, z: number, dstX: number, dstZ: number, target: number, spotanim: number, srcHeight: number, dstHeight: number, startDelay: number, endDelay: number, peak: number, arc: number) {
const event = new ZoneEvent(ServerProt.MAP_PROJANIM);
event.buffer = Zone.mapProjAnim(x, z, dstX, dstZ, target, spotanim, srcHeight, dstHeight, startDelay, endDelay, peak, arc);

View file

@ -3,10 +3,10 @@ import Zone from '#lostcity/engine/zone/Zone.js';
export default class ZoneManager {
// 256x256 mapsquares (room for instances): 2048 * 2048 * 4 = ((256 * 256) * 64) * 4
// 60x160 mapsquares (we don't support instances): ((60 * 160) * 64) * 4
private static TOTAL_ZONE_COUNT: number = ((60 * 160) * 64) * 4;
private static TOTAL_ZONE_COUNT: number = 60 * 160 * 64 * 4;
private static zoneIndex(x: number, z: number, level: number): number {
return ((x >> 3) & 0x7FF) | (((z >> 3) & 0x7FF) << 11) | ((level & 0x3) << 22);
return ((x >> 3) & 0x7ff) | (((z >> 3) & 0x7ff) << 11) | ((level & 0x3) << 22);
}
zones = new Array<Zone>(ZoneManager.TOTAL_ZONE_COUNT);

View file

@ -20,5 +20,5 @@ export default abstract class Entity {
this.length = length;
}
abstract resetEntity(respawn: boolean): void
abstract resetEntity(respawn: boolean): void;
}

View file

@ -9,7 +9,7 @@ export enum PlayerQueueType {
ENGINE,
WEAK, // sept 2004
STRONG, // late-2004
SOFT, // OSRS
SOFT // OSRS
}
export type QueueType = NpcQueueType | PlayerQueueType;

View file

@ -16,25 +16,25 @@ export interface EntityTimer {
/**
* The type of the timer.
*/
type: TimerType,
type: TimerType;
/**
* The script to execute.
*/
script: Script,
script: Script;
/**
* The arguments to execute the script with.
*/
args: ScriptArgument[] | null,
args: ScriptArgument[] | null;
/**
* The time interval between executions.
*/
interval: number,
interval: number;
/**
* Tracks the time until execution.
*/
clock: number,
clock: number;
}

View file

@ -4,4 +4,4 @@ export default abstract class NonPathingEntity extends Entity {
resetEntity(respawn: boolean) {
// nothing happens here
}
}
}

View file

@ -20,13 +20,13 @@ import NpcMode from '#lostcity/entity/NpcMode.js';
import Obj from '#lostcity/entity/Obj.js';
import PathingEntity from '#lostcity/entity/PathingEntity.js';
import Player from '#lostcity/entity/Player.js';
import {Direction, Position} from '#lostcity/entity/Position.js';
import { Direction, Position } from '#lostcity/entity/Position.js';
import HuntType from '#lostcity/cache/HuntType.js';
import HuntModeType from '#lostcity/entity/hunt/HuntModeType.js';
import HuntVis from '#lostcity/entity/hunt/HuntVis.js';
import HuntCheckNotTooStrong from '#lostcity/entity/hunt/HuntCheckNotTooStrong.js';
import {CollisionFlag} from '@2004scape/rsmod-pathfinder';
import { CollisionFlag } from '@2004scape/rsmod-pathfinder';
export default class Npc extends PathingEntity {
static ANIM = 0x2;
@ -82,12 +82,12 @@ export default class Npc extends PathingEntity {
nextHuntTick: number = -1;
interacted: boolean = false;
target: (Player | Npc | Loc | Obj | null) = null;
target: Player | Npc | Loc | Obj | null = null;
targetOp: number = -1;
heroPoints: {
uid: number,
points: number
uid: number;
points: number;
}[] = new Array(16); // be sure to reset when stats are recovered/reset
found: (Player | Npc | Loc | Obj)[] = [];
@ -318,8 +318,8 @@ export default class Npc extends PathingEntity {
}
randomWalk(range: number) {
const dx = Math.round((Math.random() * (range * 2)) - range);
const dz = Math.round((Math.random() * (range * 2)) - range);
const dx = Math.round(Math.random() * (range * 2) - range);
const dz = Math.round(Math.random() * (range * 2) - range);
const destX = this.startX + dx;
const destZ = this.startZ + dz;
@ -496,8 +496,18 @@ export default class Npc extends PathingEntity {
}
const distanceToTarget = Position.distanceTo(this, target);
const distanceToEscape = Position.distanceTo(this, { x: this.startX, z: this.startZ, width: this.width, length: this.length });
const targetDistanceFromStart = Position.distanceTo(target, { x: this.startX, z: this.startZ, width: target.width, length: target.length });
const distanceToEscape = Position.distanceTo(this, {
x: this.startX,
z: this.startZ,
width: this.width,
length: this.length
});
const targetDistanceFromStart = Position.distanceTo(target, {
x: this.startX,
z: this.startZ,
width: target.width,
length: target.length
});
const type = NpcType.get(this.type);
if (distanceToTarget > type.maxrange) {
@ -508,7 +518,8 @@ export default class Npc extends PathingEntity {
this.facePlayer(target.pid);
// todo: rework this logic
const op = (this.mode >= NpcMode.OPNPC1 && this.mode <= NpcMode.OPNPC5) ||
const op =
(this.mode >= NpcMode.OPNPC1 && this.mode <= NpcMode.OPNPC5) ||
(this.mode >= NpcMode.OPPLAYER1 && this.mode <= NpcMode.OPPLAYER5) ||
(this.mode >= NpcMode.OPLOC1 && this.mode <= NpcMode.OPLOC5) ||
(this.mode >= NpcMode.OPOBJ1 && this.mode <= NpcMode.OPOBJ5);
@ -521,7 +532,8 @@ export default class Npc extends PathingEntity {
return;
}
const ap = (this.mode >= NpcMode.APNPC1 && this.mode <= NpcMode.APNPC5) ||
const ap =
(this.mode >= NpcMode.APNPC1 && this.mode <= NpcMode.APNPC5) ||
(this.mode >= NpcMode.APPLAYER1 && this.mode <= NpcMode.APPLAYER5) ||
(this.mode >= NpcMode.APLOC1 && this.mode <= NpcMode.APLOC5) ||
(this.mode >= NpcMode.APOBJ1 && this.mode <= NpcMode.APOBJ5);
@ -682,7 +694,7 @@ export default class Npc extends PathingEntity {
}
}
setInteraction(target: (Player | Npc | Loc | Obj), op: NpcMode) {
setInteraction(target: Player | Npc | Loc | Obj, op: NpcMode) {
this.target = target;
this.targetOp = op;
this.mode = op;
@ -735,20 +747,14 @@ export default class Npc extends PathingEntity {
for (let i = 0; i < nearby.length; i++) {
const player = nearby[i];
if (hunt.checkVis === HuntVis.LINEOFSIGHT &&
!World.lineValidator.hasLineOfSight(this.level, this.x, this.z, player.x, player.z, this.width, player.width, player.length))
{
if (hunt.checkVis === HuntVis.LINEOFSIGHT && !World.lineValidator.hasLineOfSight(this.level, this.x, this.z, player.x, player.z, this.width, player.width, player.length)) {
continue;
} else if (hunt.checkVis === HuntVis.LINEOFWALK &&
!World.lineValidator.hasLineOfWalk(this.level, this.x, this.z, player.x, player.z, 1, 1, 1))
{
} else if (hunt.checkVis === HuntVis.LINEOFWALK && !World.lineValidator.hasLineOfWalk(this.level, this.x, this.z, player.x, player.z, 1, 1, 1)) {
continue;
}
// TODO: probably zone check to see if they're in the wilderness as well?
if (hunt.checkNotTooStrong === HuntCheckNotTooStrong.OUTSIDE_WILDERNESS &&
player.combatLevel > type.vislevel * 2)
{
if (hunt.checkNotTooStrong === HuntCheckNotTooStrong.OUTSIDE_WILDERNESS && player.combatLevel > type.vislevel * 2) {
continue;
}

View file

@ -92,7 +92,7 @@ enum NpcMode {
QUEUE17 = 63,
QUEUE18 = 64,
QUEUE19 = 65,
QUEUE20 = 66,
QUEUE20 = 66
}
export default NpcMode;

View file

@ -22,7 +22,7 @@ export default abstract class PathingEntity extends Entity {
walkDir: number = -1;
runDir: number = -1;
waypointIndex: number = -1;
waypoints: { x: number, z: number }[] = [];
waypoints: { x: number; z: number }[] = [];
lastX: number = -1;
lastZ: number = -1;
forceMove: boolean = false;
@ -237,7 +237,13 @@ export default abstract class PathingEntity extends Entity {
* Check if the number of tiles moved is > 2, we use Teleport for this PathingEntity.
*/
validateDistanceWalked() {
const distanceCheck = Position.distanceTo(this, { x: this.lastX, z: this.lastZ, width: this.width, length: this.length }) > 2;
const distanceCheck =
Position.distanceTo(this, {
x: this.lastX,
z: this.lastZ,
width: this.width,
length: this.length
}) > 2;
if (distanceCheck) {
this.tele = true;
this.jump = true;
@ -251,7 +257,12 @@ export default abstract class PathingEntity extends Entity {
let tele = this.tele;
// convert p_teleport() into walk or run
const distanceMoved = Position.distanceTo(this, { x: this.lastX, z: this.lastZ, width: this.width, length: this.length });
const distanceMoved = Position.distanceTo(this, {
x: this.lastX,
z: this.lastZ,
width: this.width,
length: this.length
});
if (tele && !this.jump && distanceMoved <= 2) {
if (distanceMoved === 2) {
// run
@ -282,7 +293,20 @@ export default abstract class PathingEntity extends Entity {
return this.waypointIndex === 0;
}
inOperableDistance(target: Player | Npc | Loc | Obj | { x: number, z: number, level: number, width: number, length: number }): boolean {
inOperableDistance(
target:
| Player
| Npc
| Loc
| Obj
| {
x: number;
z: number;
level: number;
width: number;
length: number;
}
): boolean {
if (!target || target.level !== this.level) {
return false;
}
@ -296,7 +320,21 @@ export default abstract class PathingEntity extends Entity {
return ReachStrategy.reached(World.collisionFlags, this.level, this.x, this.z, target.x, target.z, target.width, target.length, this.width, 0, shape);
}
inApproachDistance(range: number, target: Player | Npc | Loc | Obj | { x: number, z: number, level: number, width: number, length: number }): boolean {
inApproachDistance(
range: number,
target:
| Player
| Npc
| Loc
| Obj
| {
x: number;
z: number;
level: number;
width: number;
length: number;
}
): boolean {
if (!target || target.level !== this.level) {
return false;
}
@ -309,7 +347,7 @@ export default abstract class PathingEntity extends Entity {
}
getCollisionStrategy(): CollisionStrategy | null {
switch(this.moveRestrict) {
switch (this.moveRestrict) {
case MoveRestrict.NORMAL:
return CollisionStrategies.NORMAL;
case MoveRestrict.BLOCKED:

View file

@ -61,7 +61,7 @@ for (let i = 0; i < 99; i++) {
const level = i + 1;
const delta = Math.floor(level + Math.pow(2.0, level / 7.0) * 300.0);
acc += delta;
levelExperience[i] = Math.floor((acc / 4)) * 10;
levelExperience[i] = Math.floor(acc / 4) * 10;
}
function getLevelByExp(exp: number) {
@ -157,18 +157,35 @@ export default class Player extends PathingEntity {
static RUNECRAFT = 20;
static SKILLS = [
'attack', 'defence', 'strength', 'hitpoints', 'ranged', 'prayer',
'magic', 'cooking', 'woodcutting', 'fletching', 'fishing', 'firemaking',
'crafting', 'smithing', 'mining', 'herblore', 'agility', 'thieving',
'stat18', 'stat19', 'runecraft'
'attack',
'defence',
'strength',
'hitpoints',
'ranged',
'prayer',
'magic',
'cooking',
'woodcutting',
'fletching',
'fishing',
'firemaking',
'crafting',
'smithing',
'mining',
'herblore',
'agility',
'thieving',
'stat18',
'stat19',
'runecraft'
];
static DESIGN_BODY_COLORS: number[][] = [
[ 6798, 107, 10283, 16, 4797, 7744, 5799, 4634, 33697, 22433, 2983, 54193 ],
[ 8741, 12, 64030, 43162, 7735, 8404, 1701, 38430, 24094, 10153, 56621, 4783, 1341, 16578, 35003, 25239 ],
[ 25238, 8742, 12, 64030, 43162, 7735, 8404, 1701, 38430, 24094, 10153, 56621, 4783, 1341, 16578, 35003 ],
[ 4626, 11146, 6439, 12, 4758, 10270 ],
[ 4550, 4537, 5681, 5673, 5790, 6806, 8076, 4574 ]
[6798, 107, 10283, 16, 4797, 7744, 5799, 4634, 33697, 22433, 2983, 54193],
[8741, 12, 64030, 43162, 7735, 8404, 1701, 38430, 24094, 10153, 56621, 4783, 1341, 16578, 35003, 25239],
[25238, 8742, 12, 64030, 43162, 7735, 8404, 1701, 38430, 24094, 10153, 56621, 4783, 1341, 16578, 35003],
[4626, 11146, 6439, 12, 4758, 10270],
[4550, 4537, 5681, 5673, 5790, 6806, 8076, 4574]
];
static loadFromFile(name: string) {
@ -394,10 +411,10 @@ export default class Player extends PathingEntity {
basRunning: number = -1;
logoutRequested: boolean = false;
invListeners: {
type: number, // InvType
com: number, // IfType
source: number, // uid or -1 for world
firstSeen: boolean
type: number; // InvType
com: number; // IfType
source: number; // uid or -1 for world
firstSeen: boolean;
}[] = [];
allowDesign: boolean = false;
afkEventReady: boolean = false;
@ -454,7 +471,7 @@ export default class Player extends PathingEntity {
interacted: boolean = false;
repathed: boolean = false;
target: (Player | Npc | Loc | Obj | null) = null;
target: Player | Npc | Loc | Obj | null = null;
targetOp: number = -1;
targetSubject: number = -1; // for [opplayeru,obj]
apRange: number = 10;
@ -485,15 +502,9 @@ export default class Player extends PathingEntity {
26, // arms
33, // gloves
36, // legs
42, // boots
];
this.colors = [
0,
0,
0,
0,
0
42 // boots
];
this.colors = [0, 0, 0, 0, 0];
this.gender = 0;
this.runenergy = 10000;
this.runweight = 0;
@ -560,7 +571,7 @@ export default class Player extends PathingEntity {
if (length == -1) {
length = this.client.in[offset++];
} else if (length == -2) {
length = this.client.in[offset++] << 8 | this.client.in[offset++];
length = (this.client.in[offset++] << 8) | this.client.in[offset++];
}
decoded.push({
@ -633,7 +644,17 @@ export default class Player extends PathingEntity {
pathfindZ = data.g1s() + startZ;
}
if (this.delayed() || running < 0 || running > 1 || Position.distanceTo(this, { x: pathfindX, z: pathfindZ, width: this.width, length: this.length }) > 104) {
if (
this.delayed() ||
running < 0 ||
running > 1 ||
Position.distanceTo(this, {
x: pathfindX,
z: pathfindZ,
width: this.width,
length: this.length
}) > 104
) {
pathfindX = -1;
pathfindZ = -1;
this.unsetMapFlag();
@ -816,11 +837,12 @@ export default class Player extends PathingEntity {
continue;
}
if (opcode === ClientProt.INV_BUTTON1 && !com.inventoryOptions[0] ||
opcode === ClientProt.INV_BUTTON2 && !com.inventoryOptions[1] ||
opcode === ClientProt.INV_BUTTON3 && !com.inventoryOptions[2] ||
opcode === ClientProt.INV_BUTTON4 && !com.inventoryOptions[3] ||
opcode === ClientProt.INV_BUTTON5 && !com.inventoryOptions[4]
if (
(opcode === ClientProt.INV_BUTTON1 && !com.inventoryOptions[0]) ||
(opcode === ClientProt.INV_BUTTON2 && !com.inventoryOptions[1]) ||
(opcode === ClientProt.INV_BUTTON3 && !com.inventoryOptions[2]) ||
(opcode === ClientProt.INV_BUTTON4 && !com.inventoryOptions[3]) ||
(opcode === ClientProt.INV_BUTTON5 && !com.inventoryOptions[4])
) {
continue;
}
@ -916,11 +938,7 @@ export default class Player extends PathingEntity {
}
const type = ObjType.get(item);
if (opcode === ClientProt.OPHELD1 && !type.iops[0] ||
opcode === ClientProt.OPHELD2 && !type.iops[1] ||
opcode === ClientProt.OPHELD3 && !type.iops[2] ||
opcode === ClientProt.OPHELD4 && !type.iops[3]
) {
if ((opcode === ClientProt.OPHELD1 && !type.iops[0]) || (opcode === ClientProt.OPHELD2 && !type.iops[1]) || (opcode === ClientProt.OPHELD3 && !type.iops[2]) || (opcode === ClientProt.OPHELD4 && !type.iops[3])) {
continue;
}
@ -1122,11 +1140,12 @@ export default class Player extends PathingEntity {
}
const locType = LocType.get(loc.type);
if (opcode === ClientProt.OPLOC1 && !locType.ops[0] ||
opcode === ClientProt.OPLOC2 && !locType.ops[1] ||
opcode === ClientProt.OPLOC3 && !locType.ops[2] ||
opcode === ClientProt.OPLOC4 && !locType.ops[3] ||
opcode === ClientProt.OPLOC5 && !locType.ops[4]
if (
(opcode === ClientProt.OPLOC1 && !locType.ops[0]) ||
(opcode === ClientProt.OPLOC2 && !locType.ops[1]) ||
(opcode === ClientProt.OPLOC3 && !locType.ops[2]) ||
(opcode === ClientProt.OPLOC4 && !locType.ops[3]) ||
(opcode === ClientProt.OPLOC5 && !locType.ops[4])
) {
continue;
}
@ -1247,11 +1266,12 @@ export default class Player extends PathingEntity {
}
const npcType = NpcType.get(npc.type);
if (opcode === ClientProt.OPNPC1 && !npcType.ops[0] ||
opcode === ClientProt.OPNPC2 && !npcType.ops[1] ||
opcode === ClientProt.OPNPC3 && !npcType.ops[2] ||
opcode === ClientProt.OPNPC4 && !npcType.ops[3] ||
opcode === ClientProt.OPNPC5 && !npcType.ops[4]
if (
(opcode === ClientProt.OPNPC1 && !npcType.ops[0]) ||
(opcode === ClientProt.OPNPC2 && !npcType.ops[1]) ||
(opcode === ClientProt.OPNPC3 && !npcType.ops[2]) ||
(opcode === ClientProt.OPNPC4 && !npcType.ops[3]) ||
(opcode === ClientProt.OPNPC5 && !npcType.ops[4])
) {
continue;
}
@ -1366,9 +1386,7 @@ export default class Player extends PathingEntity {
const objType = ObjType.get(obj.type);
// todo: validate all options
if (opcode === ClientProt.OPOBJ1 && !objType.ops[0] ||
opcode === ClientProt.OPOBJ4 && !objType.ops[3]
) {
if ((opcode === ClientProt.OPOBJ1 && !objType.ops[0]) || (opcode === ClientProt.OPOBJ4 && !objType.ops[3])) {
continue;
}
@ -1690,7 +1708,7 @@ export default class Player extends PathingEntity {
const out = this.netOut[j];
if (this.client.encryptor) {
out.data[0] = (out.data[0] + this.client.encryptor.nextInt()) & 0xFF;
out.data[0] = (out.data[0] + this.client.encryptor.nextInt()) & 0xff;
}
World.lastCycleBandwidth[1] += out.length;
@ -1707,7 +1725,7 @@ export default class Player extends PathingEntity {
}
if (this.client.encryptor) {
packet.data[0] = (packet.data[0] + this.client.encryptor.nextInt()) & 0xFF;
packet.data[0] = (packet.data[0] + this.client.encryptor.nextInt()) & 0xff;
}
this.client.write(packet);
@ -1821,7 +1839,8 @@ export default class Player extends PathingEntity {
const count = ScriptProvider.load('data/pack/server');
this.messageGame(`Reloaded ${count} scripts.`);
} break;
break;
}
case 'setvar': {
const varp = args.shift();
if (!varp) {
@ -1842,7 +1861,8 @@ export default class Player extends PathingEntity {
} else {
this.messageGame(`Unknown var ${varp}`);
}
} break;
break;
}
case 'getvar': {
const varp = args.shift();
if (!varp) {
@ -1856,7 +1876,8 @@ export default class Player extends PathingEntity {
} else {
this.messageGame(`Unknown var ${varp}`);
}
} break;
break;
}
case 'setlevel': {
if (args.length < 2) {
this.messageGame('Usage: ::setlevel <stat> <level>');
@ -1870,7 +1891,8 @@ export default class Player extends PathingEntity {
}
this.setLevel(stat, parseInt(args[1]));
} break;
break;
}
case 'setxp': {
if (args.length < 2) {
this.messageGame('Usage: ::setxp <stat> <xp>');
@ -1886,7 +1908,8 @@ export default class Player extends PathingEntity {
const exp = parseInt(args[1]) * 10;
this.setLevel(stat, getLevelByExp(exp));
this.stats[stat] = exp;
} break;
break;
}
case 'minlevel': {
for (let i = 0; i < Player.SKILLS.length; i++) {
if (i === Player.HITPOINTS) {
@ -1895,11 +1918,13 @@ export default class Player extends PathingEntity {
this.setLevel(i, 1);
}
}
} break;
break;
}
case 'serverdrop': {
this.client?.terminate();
this.client = null;
} break;
break;
}
default: {
if (cmd.length <= 0) {
return;
@ -1921,43 +1946,51 @@ export default class Player extends PathingEntity {
const value = args.shift();
params.push(value ?? '');
} break;
break;
}
case ScriptVarType.INT: {
const value = args.shift();
// todo: range check? runtime only operates on 32-bits
params.push(parseInt(value ?? '0', 10));
} break;
break;
}
case ScriptVarType.NAMEDOBJ: {
const name = args.shift();
params.push(ObjType.getId(name ?? ''));
} break;
break;
}
case ScriptVarType.NPC: {
const name = args.shift();
params.push(NpcType.getId(name ?? ''));
} break;
break;
}
case ScriptVarType.LOC: {
const name = args.shift();
params.push(LocType.getId(name ?? ''));
} break;
break;
}
case ScriptVarType.SEQ: {
const name = args.shift();
params.push(SeqType.getId(name ?? ''));
} break;
break;
}
case ScriptVarType.STAT: {
const name = args.shift();
params.push(Player.SKILLS.indexOf(name ?? ''));
} break;
break;
}
case ScriptVarType.INV: {
const name = args.shift();
params.push(InvType.getId(name ?? ''));
} break;
break;
}
case ScriptVarType.COORD: {
const args2 = cheat.split('_');
@ -1968,17 +2001,20 @@ export default class Player extends PathingEntity {
const lz = parseInt(args2[4]);
params.push(Position.packCoord(level, (mx << 6) + lx, (mz << 6) + lz));
} break;
break;
}
case ScriptVarType.INTERFACE: {
const name = args.shift();
params.push(IfType.getId(name ?? ''));
} break;
break;
}
}
}
this.executeScript(ScriptRunner.init(script, this, null, null, params), true);
} break;
break;
}
}
}
@ -2019,7 +2055,7 @@ export default class Player extends PathingEntity {
if (this.target) {
const apTrigger = this.getApTrigger();
const outOfRange = (!this.inApproachDistance(this.apRange, this.target) && apTrigger) && !this.inOperableDistance(this.target);
const outOfRange = !this.inApproachDistance(this.apRange, this.target) && apTrigger && !this.inOperableDistance(this.target);
const targetMoved = this.hasWaypoints() && (this.waypoints[0].x !== this.target.x || this.waypoints[0].z !== this.target.z);
// broken out to understand better
@ -2082,7 +2118,7 @@ export default class Player extends PathingEntity {
// TODO: "Moving to an adjacent tile through walking will not drain energy, even if Run is turned on."
const weightKg = Math.floor(this.runweight / 1000);
const clampWeight = Math.min(Math.max(weightKg, 0), 64);
const loss = 67 + ((67 * clampWeight) / 64);
const loss = 67 + (67 * clampWeight) / 64;
this.runenergy = Math.max(this.runenergy - loss, 0);
if (this.runenergy === 0) {
@ -2093,7 +2129,7 @@ export default class Player extends PathingEntity {
}
if (!this.delayed() && (!moved || !running) && this.runenergy < 10000) {
const recovered = (this.baseLevels[Player.AGILITY] / 9) + 8;
const recovered = this.baseLevels[Player.AGILITY] / 9 + 8;
this.runenergy = Math.min(this.runenergy + recovered, 10000);
}
@ -2294,7 +2330,7 @@ export default class Player extends PathingEntity {
}
}
setInteraction(target: (Player | Npc | Loc | Obj), op: ServerTriggerType, subject?: number) {
setInteraction(target: Player | Npc | Loc | Obj, op: ServerTriggerType, subject?: number) {
if (this.forceMove || this.delayed()) {
// console.log('not setting interaction');
this.unsetMapFlag();
@ -2318,12 +2354,12 @@ export default class Player extends PathingEntity {
this.mask |= Player.FACE_ENTITY;
} else if (target instanceof Loc) {
const type = LocType.get(target.type);
this.faceX = (target.x * 2) + type.width;
this.faceZ = (target.z * 2) + type.length;
this.faceX = target.x * 2 + type.width;
this.faceZ = target.z * 2 + type.length;
this.mask |= Player.FACE_COORD;
} else {
this.faceX = (target.x * 2) + 1;
this.faceZ = (target.z * 2) + 1;
this.faceX = target.x * 2 + 1;
this.faceZ = target.z * 2 + 1;
this.mask |= Player.FACE_COORD;
}
}
@ -2529,7 +2565,7 @@ export default class Player extends PathingEntity {
}
if (!this.interacted && !this.hasWaypoints() && !moved) {
this.messageGame('I can\'t reach that!');
this.messageGame("I can't reach that!");
this.clearInteraction();
}
@ -2662,7 +2698,7 @@ export default class Player extends PathingEntity {
// update local player
bitBlock.bits();
bitBlock.pBit(1, (tele || walkDir !== -1 || runDir !== -1 || this.mask > 0) ? 1 : 0);
bitBlock.pBit(1, tele || walkDir !== -1 || runDir !== -1 || this.mask > 0 ? 1 : 0);
if (tele) {
bitBlock.pBit(2, 3);
bitBlock.pBit(2, this.level);
@ -2719,7 +2755,7 @@ export default class Player extends PathingEntity {
hasMaskUpdate = false;
}
bitBlock.pBit(1, (walkDir !== -1 || runDir !== -1 || hasMaskUpdate) ? 1 : 0);
bitBlock.pBit(1, walkDir !== -1 || runDir !== -1 || hasMaskUpdate ? 1 : 0);
if (runDir !== -1) {
bitBlock.pBit(2, 2);
bitBlock.pBit(3, walkDir);
@ -2899,15 +2935,15 @@ export default class Player extends PathingEntity {
if (newlyObserved && (this.orientation != -1 || this.faceX != -1 || this.faceZ != -1)) {
mask |= Player.FACE_COORD;
}
if (newlyObserved && (this.faceEntity != -1)) {
if (newlyObserved && this.faceEntity != -1) {
mask |= Player.FACE_ENTITY;
}
if (mask > 0xFF) {
if (mask > 0xff) {
mask |= 0x80;
}
if (self && (mask & Player.CHAT)) {
if (self && mask & Player.CHAT) {
// don't echo back local chat
mask &= ~Player.CHAT;
}
@ -2966,20 +3002,20 @@ export default class Player extends PathingEntity {
if (newlyObserved && (this.orientation != -1 || this.faceX != -1 || this.faceZ != -1)) {
mask |= Player.FACE_COORD;
}
if (newlyObserved && (this.faceEntity != -1)) {
if (newlyObserved && this.faceEntity != -1) {
mask |= Player.FACE_ENTITY;
}
if (mask > 0xFF) {
if (mask > 0xff) {
mask |= 0x80;
}
if (self && (mask & Player.CHAT)) {
if (self && mask & Player.CHAT) {
// don't echo back local chat
mask &= ~Player.CHAT;
}
out.p1(mask & 0xFF);
out.p1(mask & 0xff);
if (mask & 0x80) {
out.p1(mask >> 8);
}
@ -3141,7 +3177,7 @@ export default class Player extends PathingEntity {
hasMaskUpdate = false;
}
bitBlock.pBit(1, (runDir !== -1 || walkDir !== -1 || hasMaskUpdate) ? 1 : 0);
bitBlock.pBit(1, runDir !== -1 || walkDir !== -1 || hasMaskUpdate ? 1 : 0);
if (runDir !== -1) {
bitBlock.pBit(2, 2);
bitBlock.pBit(3, walkDir);
@ -3458,7 +3494,7 @@ export default class Player extends PathingEntity {
if (objType.certtemplate >= 0 && objType.certlink >= 0) {
uncert = objType.certlink;
}
if (objType.stackable || (uncert != obj) || container.stackType == Inventory.ALWAYS_STACK) {
if (objType.stackable || uncert != obj || container.stackType == Inventory.ALWAYS_STACK) {
const stockObj = InvType.get(inv).stockobj.includes(obj);
if (this.invTotal(inv, obj) == 0 && this.invFreeSpace(inv) == 0 && !stockObj) {
return count;
@ -3522,7 +3558,10 @@ export default class Player extends PathingEntity {
throw new Error(`invMoveFromSlot: Invalid from obj was null. This means the obj does not exist at this slot: ${fromSlot}`);
}
return {overflow: fromObj.count - this.invAdd(toInv, fromObj.id, fromObj.count), fromObj: fromObj.id};
return {
overflow: fromObj.count - this.invAdd(toInv, fromObj.id, fromObj.count),
fromObj: fromObj.id
};
}
invTotalCat(inv: number, category: number): number {
@ -3893,14 +3932,14 @@ export default class Player extends PathingEntity {
out.p2(zoneZ);
// build area is 13x13 zones (8*13 = 104 tiles), so we need to load 6 zones in each direction
const areas: { mapsquareX: number, mapsquareZ: number }[] = [];
const areas: { mapsquareX: number; mapsquareZ: number }[] = [];
for (let x = zoneX - 6; x <= zoneX + 6; x++) {
for (let z = zoneZ - 6; z <= zoneZ + 6; z++) {
const mapsquareX = Position.mapsquare(x << 3);
const mapsquareZ = Position.mapsquare(z << 3);
if (areas.findIndex(a => a.mapsquareX === mapsquareX && a.mapsquareZ === mapsquareZ) === -1) {
areas.push({mapsquareX, mapsquareZ});
areas.push({ mapsquareX, mapsquareZ });
}
}
}

View file

@ -6,10 +6,10 @@ export const Direction = {
EAST: 4,
SOUTH_WEST: 5,
SOUTH: 6,
SOUTH_EAST: 7,
SOUTH_EAST: 7
};
// TODO (jkm) consider making this an enum
type Direction = typeof Direction[keyof typeof Direction];
type Direction = (typeof Direction)[keyof typeof Direction];
// TODO (jkm) consider making this a class
export const Position = {
@ -20,7 +20,7 @@ export const Position = {
mapsquare: (pos: number) => pos >> 6,
local: (pos: number) => pos - (Position.zoneCenter(pos) << 3),
localOrigin: (pos: number) => pos - (Position.mapsquare(pos) << 6),
zoneUpdate: (pos: number) => pos - (pos >> 3 << 3),
zoneUpdate: (pos: number) => pos - ((pos >> 3) << 3),
face: (srcX: number, srcZ: number, dstX: number, dstZ: number) => {
if (srcX == dstX) {
@ -58,13 +58,13 @@ export const Position = {
return pos + Position.deltaZ(dir);
},
distanceTo(pos: { x: number, z: number, width: number, length: number }, other: { x: number, z: number, width: number, length: number }) {
distanceTo(pos: { x: number; z: number; width: number; length: number }, other: { x: number; z: number; width: number; length: number }) {
const p1 = Position.closest(pos, other);
const p2 = Position.closest(other, pos);
return Math.max(Math.abs(p1.x - p2.x), Math.abs(p1.z - p2.z));
},
closest(pos: { x: number, z: number, width: number, length: number }, other: { x: number, z: number, width: number, length: number }) {
closest(pos: { x: number; z: number; width: number; length: number }, other: { x: number; z: number; width: number; length: number }) {
const occupiedX = pos.x + pos.width - 1;
const occupiedZ = pos.z + pos.length - 1;
return {
@ -101,7 +101,7 @@ export const Position = {
return 0;
},
unpackCoord(coord: number): {level: number, x: number, z: number} {
unpackCoord(coord: number): { level: number; x: number; z: number } {
const level = (coord >> 28) & 0x3;
const x = (coord >> 14) & 0x3fff;
const z = coord & 0x3fff;
@ -109,6 +109,6 @@ export const Position = {
},
packCoord(level: number, x: number, z: number): number {
return ((z & 0x3fff) | ((x & 0x3fff) << 14) | (level & 0x3) << 28);
return (z & 0x3fff) | ((x & 0x3fff) << 14) | ((level & 0x3) << 28);
}
} as const;
} as const;

View file

@ -5,7 +5,7 @@ import { FriendServer } from '#lostcity/server/FriendServer.js';
if (!fs.existsSync('.env')) {
console.error('Missing .env file');
console.error('Please make sure you have a .env file in the main directory, copy and rename .env.example if you don\'t have one');
console.error("Please make sure you have a .env file in the main directory, copy and rename .env.example if you don't have one");
process.exit(1);
}

View file

@ -5,7 +5,7 @@ import { LoginServer } from '#lostcity/server/LoginServer.js';
if (!fs.existsSync('.env')) {
console.error('Missing .env file');
console.error('Please make sure you have a .env file in the main directory, copy and rename .env.example if you don\'t have one');
console.error("Please make sure you have a .env file in the main directory, copy and rename .env.example if you don't have one");
process.exit(1);
}

View file

@ -10,7 +10,7 @@ import WSMaintenanceServer from '#lostcity/server/WSMaintenanceServer.js';
if (!fs.existsSync('.env')) {
console.error('Missing .env file');
console.error('Please make sure you have a .env file in the main directory, copy and rename .env.example if you don\'t have one');
console.error("Please make sure you have a .env file in the main directory, copy and rename .env.example if you don't have one");
process.exit(1);
}

View file

@ -91,7 +91,7 @@ export enum ClientProt {
FRIENDLIST_ADD = 118, // index: 252 - NXT naming
CLIENT_CHEAT = 4, // index: 253 - NXT naming
MESSAGE_PUBLIC = 158, // index: 254 - NXT naming
MOVE_GAMECLICK = 181, // index: 255 - NXT naming
MOVE_GAMECLICK = 181 // index: 255 - NXT naming
}
export const ClientProtLengths: number[] = [];

View file

@ -41,7 +41,7 @@ export default class ClientSocket {
this.type = type;
this.state = state;
}
isTCP() {
return this.type === ClientSocket.TCP;
}

View file

@ -15,10 +15,10 @@ export class FriendServer {
private players: bigint[][] = [];
private messageId: number = 0;
private messages: {
sender: bigint
messageId: number
staffModLevel: number
text: string
sender: bigint;
messageId: number;
staffModLevel: number;
text: string;
}[][] = [];
private loggedInEvents: bigint[][] = [];
private loggedOutEvents: bigint[][] = [];
@ -126,85 +126,67 @@ export class FriendServer {
const player = data.g8();
const other = data.g8();
const playerAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(player))
.selectAll().executeTakeFirst();
const otherAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(other))
.selectAll().executeTakeFirst();
const playerAcc = await db.selectFrom('account').where('username', '=', fromBase37(player)).selectAll().executeTakeFirst();
const otherAcc = await db.selectFrom('account').where('username', '=', fromBase37(other)).selectAll().executeTakeFirst();
if (!playerAcc || !otherAcc) {
return;
}
await db.insertInto('friendlist')
await db
.insertInto('friendlist')
.values({
account_id: playerAcc.id,
friend_account_id: otherAcc?.id
})
.ignore().execute();
.ignore()
.execute();
} else if (opcode === 7) {
// del friend
const player = data.g8();
const other = data.g8();
const playerAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(player))
.selectAll().executeTakeFirst();
const otherAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(other))
.selectAll().executeTakeFirst();
const playerAcc = await db.selectFrom('account').where('username', '=', fromBase37(player)).selectAll().executeTakeFirst();
const otherAcc = await db.selectFrom('account').where('username', '=', fromBase37(other)).selectAll().executeTakeFirst();
if (!playerAcc || !otherAcc) {
return;
}
await db.deleteFrom('friendlist')
.where('account_id', '=', playerAcc.id)
.where('friend_account_id', '=', otherAcc.id)
.execute();
await db.deleteFrom('friendlist').where('account_id', '=', playerAcc.id).where('friend_account_id', '=', otherAcc.id).execute();
} else if (opcode === 8) {
// add ignore
const player = data.g8();
const other = data.g8();
const playerAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(player))
.selectAll().executeTakeFirst();
const otherAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(other))
.selectAll().executeTakeFirst();
const playerAcc = await db.selectFrom('account').where('username', '=', fromBase37(player)).selectAll().executeTakeFirst();
const otherAcc = await db.selectFrom('account').where('username', '=', fromBase37(other)).selectAll().executeTakeFirst();
if (!playerAcc || !otherAcc) {
return;
}
await db.insertInto('ignorelist')
await db
.insertInto('ignorelist')
.values({
account_id: playerAcc.id,
ignore_account_id: otherAcc?.id
})
.ignore().execute();
.ignore()
.execute();
} else if (opcode === 9) {
// del ignore
const player = data.g8();
const other = data.g8();
const playerAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(player))
.selectAll().executeTakeFirst();
const otherAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(other))
.selectAll().executeTakeFirst();
const playerAcc = await db.selectFrom('account').where('username', '=', fromBase37(player)).selectAll().executeTakeFirst();
const otherAcc = await db.selectFrom('account').where('username', '=', fromBase37(other)).selectAll().executeTakeFirst();
if (!playerAcc || !otherAcc) {
return;
}
await db.deleteFrom('ignorelist')
.where('account_id', '=', playerAcc.id)
.where('ignore_account_id', '=', otherAcc.id)
.execute();
await db.deleteFrom('ignorelist').where('account_id', '=', playerAcc.id).where('ignore_account_id', '=', otherAcc.id).execute();
} else if (opcode === 10) {
// log private message
const world = data.g2();
@ -220,13 +202,9 @@ export class FriendServer {
}
}
const fromAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(from))
.selectAll().executeTakeFirst();
const toAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(to))
.selectAll().executeTakeFirst();
const fromAcc = await db.selectFrom('account').where('username', '=', fromBase37(from)).selectAll().executeTakeFirst();
const toAcc = await db.selectFrom('account').where('username', '=', fromBase37(to)).selectAll().executeTakeFirst();
if (toWorld == -1 || !fromAcc || !toAcc) {
return;
@ -246,36 +224,38 @@ export class FriendServer {
text: message
});
await db.insertInto('private_chat')
await db
.insertInto('private_chat')
.values({
from_account_id: fromAcc.id,
to_account_id: toAcc.id,
message: message
}).execute();
})
.execute();
} else if (opcode === 11) {
// log public message
const world = data.g2();
const from = data.g8();
const message = data.gjstr();
const fromAcc = await db.selectFrom('account')
.where('username', '=', fromBase37(from))
.selectAll().executeTakeFirst();
const fromAcc = await db.selectFrom('account').where('username', '=', fromBase37(from)).selectAll().executeTakeFirst();
if (!fromAcc) {
return;
}
await db.insertInto('public_chat')
await db
.insertInto('public_chat')
.values({
account_id: fromAcc.id,
message: message
}).execute();
})
.execute();
}
});
socket.on('close', () => { });
socket.on('error', () => { });
socket.on('close', () => {});
socket.on('error', () => {});
});
this.server.listen({ port: Environment.FRIEND_PORT, host: '0.0.0.0' }, () => {

View file

@ -16,47 +16,58 @@ parentPort.on('message', async msg => {
case 'reset': {
const client = new FriendClient();
await client.reset();
} break;
break;
}
case 'heartbeat': {
const client = new FriendClient();
await client.heartbeat(msg.players);
} break;
break;
}
case 'login': {
const client = new FriendClient();
await client.login(msg.username);
} break;
break;
}
case 'logout': {
const client = new FriendClient();
await client.logout(msg.username);
} break;
break;
}
case 'latest': {
// const client = new FriendClient();
// const { messages, loggedInEvents, loggedOutEvents } = await client.latest();
} break;
break;
}
case 'addfriend': {
const client = new FriendClient();
await client.addFriend(msg.player, msg.other);
} break;
break;
}
case 'delfriend': {
const client = new FriendClient();
await client.delFriend(msg.player, msg.other);
} break;
break;
}
case 'addignore': {
const client = new FriendClient();
await client.addIgnore(msg.player, msg.other);
} break;
break;
}
case 'delignore': {
const client = new FriendClient();
await client.delIgnore(msg.player, msg.other);
} break;
break;
}
case 'private_message': {
const client = new FriendClient();
await client.privateMessage(msg.from, msg.to, msg.text);
} break;
break;
}
case 'public_message': {
const client = new FriendClient();
await client.publicMessage(msg.from, msg.text);
} break;
break;
}
default:
console.error('Unknown message type: ' + msg.type);
break;

View file

@ -59,10 +59,8 @@ export class LoginServer {
}
const username = fromBase37(username37);
const account = await db.selectFrom('account')
.where('username', '=', username)
.selectAll().executeTakeFirst();
if (!account || await bcrypt.compare(password.toLowerCase(), account.password) === false) {
const account = await db.selectFrom('account').where('username', '=', username).selectAll().executeTakeFirst();
if (!account || (await bcrypt.compare(password.toLowerCase(), account.password)) === false) {
// invalid credentials (bad user or bad pass)
const reply = new Packet();
reply.p1(5);
@ -163,8 +161,8 @@ export class LoginServer {
}
});
socket.on('close', () => { });
socket.on('error', () => { });
socket.on('close', () => {});
socket.on('error', () => {});
});
this.server.listen({ port: Environment.LOGIN_PORT, host: '0.0.0.0' }, () => {
@ -277,7 +275,7 @@ export class LoginClient {
}
}
async load(username37: bigint, password: string, uid: number): Promise<{ reply: number, data: Packet | null }> {
async load(username37: bigint, password: string, uid: number): Promise<{ reply: number; data: Packet | null }> {
await this.connect();
if (this.socket === null) {

View file

@ -20,7 +20,7 @@ const priv = forge.pki.privateKeyFromPem(fs.readFileSync('data/config/private.pe
parentPort.on('message', async msg => {
try {
if (!parentPort) throw new Error('This file must be run as a worker thread.');
switch (msg.type) {
case 'reset': {
if (!Environment.LOGIN_KEY) {
@ -29,7 +29,8 @@ parentPort.on('message', async msg => {
const login = new LoginClient();
await login.reset();
} break;
break;
}
case 'heartbeat': {
if (!Environment.LOGIN_KEY) {
return;
@ -37,7 +38,8 @@ parentPort.on('message', async msg => {
const login = new LoginClient();
await login.heartbeat(msg.players);
} break;
break;
}
case 'loginreq': {
const { opcode, data, socket } = msg;
@ -183,14 +185,15 @@ parentPort.on('message', async msg => {
save
});
}
} break;
break;
}
case 'logout': {
const { username, save } = msg;
if (Environment.LOGIN_KEY) {
const login = new LoginClient();
const reply = await login.save(toBase37(username), save);
if (reply === 0) {
parentPort.postMessage({
type: 'logoutreply',
@ -203,7 +206,8 @@ parentPort.on('message', async msg => {
username
});
}
} break;
break;
}
default:
console.error('Unknown message type: ' + msg.type);
break;

View file

@ -112,36 +112,16 @@ export enum ServerProt {
// TODO: do this packet-by-packet instead of all at once
export const ServerProtLengths: number[] = [
0, -2, 4, 6, -1, 0, 0, 2, 0, 0,
0, 0, 5, 4, 2, 2, 0, 0, 0, 0,
2, -2, 2, 14, 0, 6, 3, 0, 4, 0,
0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
0, -1, 4, 2, 6, 0, 6, 0, 0, 3,
7, 0, 0, 0, -1, 0, 0, 0, 0, 4,
0, 0, 0, 0, 0, 0, 0, 0, 1, 15,
0, 0, 0, 0, 6, 0, 2, 0, 0, 0,
2, 0, 0, 0, 1, 0, 0, 4, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, -2, 0,
0, 0, 0, 6, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, -2, 0, 0, 2, 0, 0, 0, 2,
9, 0, 0, 0, 0, 0, 4, 0, 0, 0,
3, 7, 9, 0, 0, 0, 0, 0, 0, 0,
0, 0, -2, 0, 0, 0, 0, 3, 2, 0,
0, 0, 0, 0, 0, 6, 0, 0, 0, 0,
0, 0, 0, 0, -2, 2, 0, 0, 0, 0,
0, 6, 0, 0, 0, 2, 0, 2, 0, 0,
0, -2, 0, 0, 4, 0, 0, 0, 0, 6,
0, 0, -2, -2, 0, 0, 0, 0, 0, 0,
-2, 0, 0, 5, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, -2, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0
0, -2, 4, 6, -1, 0, 0, 2, 0, 0, 0, 0, 5, 4, 2, 2, 0, 0, 0, 0, 2, -2, 2, 14, 0, 6, 3, 0, 4, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, -1, 4, 2, 6, 0, 6, 0, 0, 3, 7, 0, 0, 0, -1, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 15, 0, 0, 0, 0, 6, 0, 2, 0, 0, 0,
2, 0, 0, 0, 1, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -2, 0, 0, 2, 0, 0, 0, 2, 9, 0, 0, 0, 0, 0, 4, 0, 0, 0, 3, 7, 9, 0, 0, 0, 0, 0, 0, 0, 0,
0, -2, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, -2, 2, 0, 0, 0, 0, 0, 6, 0, 0, 0, 2, 0, 2, 0, 0, 0, -2, 0, 0, 4, 0, 0, 0, 0, 6, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, -2, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
];
// TODO: come up with another solution that preserves type-safety?
export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[]) => void } = {
export const ServerProtEncoders: {
[key: number]: (buf: Packet, ...args: any[]) => void;
} = {
[ServerProt.IF_OPENCHATMODAL]: (buf: Packet, com: number) => {
buf.p2(com);
},
@ -150,8 +130,7 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
buf.p2(comSide);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.IF_CLOSE]: (_buf: Packet) => {
},
[ServerProt.IF_CLOSE]: (_buf: Packet) => {},
[ServerProt.IF_OPENSIDEOVERLAY]: (buf: Packet, com: number, tab: number) => {
buf.p2(com);
buf.p1(tab);
@ -288,8 +267,7 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
buf.p1(stepScale);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.CAM_RESET]: (_buf: Packet) => {
},
[ServerProt.CAM_RESET]: (_buf: Packet) => {},
[ServerProt.NPC_INFO]: (buf: Packet, bitBlock: Packet, byteBlock: Packet) => {
buf.pdata(bitBlock);
@ -301,11 +279,9 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.FINISH_TRACKING]: (_buf: Packet) => {
},
[ServerProt.FINISH_TRACKING]: (_buf: Packet) => {},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.ENABLE_TRACKING]: (_buf: Packet) => {
},
[ServerProt.ENABLE_TRACKING]: (_buf: Packet) => {},
[ServerProt.MESSAGE_GAME]: (buf: Packet, msg: string) => {
buf.pjstr(msg);
@ -332,8 +308,7 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.UNSET_MAP_FLAG]: (_buf: Packet) => {
},
[ServerProt.UNSET_MAP_FLAG]: (_buf: Packet) => {},
[ServerProt.UPDATE_RUNWEIGHT]: (buf: Packet, kg: number) => {
buf.p2(kg);
},
@ -378,8 +353,7 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
buf.p1((energy / 100) | 0);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.RESET_ANIMS]: (_buf: Packet) => {
},
[ServerProt.RESET_ANIMS]: (_buf: Packet) => {},
[ServerProt.UPDATE_UID192]: (buf: Packet, uid: number) => {
buf.p2(uid);
},
@ -390,11 +364,9 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
buf.p2(unreadMessageCount);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.LOGOUT]: (_buf: Packet) => {
},
[ServerProt.LOGOUT]: (_buf: Packet) => {},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.P_COUNTDIALOG]: (_buf: Packet) => {
},
[ServerProt.P_COUNTDIALOG]: (_buf: Packet) => {},
[ServerProt.SET_MULTIWAY]: (buf: Packet, state: boolean) => {
buf.pbool(state);
},
@ -442,8 +414,7 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
buf.p4(value);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[ServerProt.RESET_CLIENT_VARCACHE]: (_buf: Packet) => {
},
[ServerProt.RESET_CLIENT_VARCACHE]: (_buf: Packet) => {},
[ServerProt.SYNTH_SOUND]: (buf: Packet, sound: number, loops: number, delay: number) => {
buf.p2(sound);
@ -477,11 +448,7 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
// merge player with loc, e.g. agility training through pipes
// useful for draw prioritizes
[ServerProt.LOC_MERGE]: (buf: Packet, srcX: number, srcZ: number,
shape: number, angle: number, loc: number,
startCycle: number, endCycle: number,
pid: number, east: number, south: number, west: number, north: number) =>
{
[ServerProt.LOC_MERGE]: (buf: Packet, srcX: number, srcZ: number, shape: number, angle: number, loc: number, startCycle: number, endCycle: number, pid: number, east: number, south: number, west: number, north: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p1((shape << 2) | (angle & 3));
buf.p2(loc);
@ -493,41 +460,27 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
buf.p1(west - srcX);
buf.p1(north - srcZ);
},
[ServerProt.LOC_ANIM]: (buf: Packet, srcX: number, srcZ: number,
shape: number, angle: number, loc: number) =>
{
[ServerProt.LOC_ANIM]: (buf: Packet, srcX: number, srcZ: number, shape: number, angle: number, loc: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p1((shape << 2) | (angle & 3));
buf.p2(loc);
},
[ServerProt.OBJ_DEL]: (buf: Packet, srcX: number, srcZ: number,
obj: number) =>
{
[ServerProt.OBJ_DEL]: (buf: Packet, srcX: number, srcZ: number, obj: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p2(obj);
},
[ServerProt.OBJ_REVEAL]: (buf: Packet, srcX: number, srcZ: number,
obj: number, count: number, owner: number) =>
{
[ServerProt.OBJ_REVEAL]: (buf: Packet, srcX: number, srcZ: number, obj: number, count: number, owner: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p2(obj);
buf.p2(count);
buf.p2(owner);
},
[ServerProt.LOC_ADD_CHANGE]: (buf: Packet, srcX: number, srcZ: number,
shape: number, angle: number, loc: number) =>
{
[ServerProt.LOC_ADD_CHANGE]: (buf: Packet, srcX: number, srcZ: number, shape: number, angle: number, loc: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p1((shape << 2) | (angle & 3));
buf.p2(loc);
},
[ServerProt.MAP_PROJANIM]: (buf: Packet, srcX: number, srcZ: number,
dstX: number, dstZ: number,
target: number, spotanim: number,
srcHeight: number, dstHeight: number,
startDelay: number, endDelay: number,
peak: number, arc: number) =>
{
[ServerProt.MAP_PROJANIM]: (buf: Packet, srcX: number, srcZ: number, dstX: number, dstZ: number, target: number, spotanim: number, srcHeight: number, dstHeight: number, startDelay: number, endDelay: number, peak: number, arc: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p1(dstX - srcX);
buf.p1(dstZ - srcZ);
@ -540,30 +493,22 @@ export const ServerProtEncoders: { [key: number]: (buf: Packet, ...args: any[])
buf.p1(peak);
buf.p1(arc);
},
[ServerProt.LOC_DEL]: (buf: Packet, srcX: number, srcZ: number,
shape: number, angle: number) =>
{
[ServerProt.LOC_DEL]: (buf: Packet, srcX: number, srcZ: number, shape: number, angle: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p1((shape << 2) | (angle & 3));
},
[ServerProt.OBJ_COUNT]: (buf: Packet, srcX: number, srcZ: number,
obj: number, count: number) =>
{
[ServerProt.OBJ_COUNT]: (buf: Packet, srcX: number, srcZ: number, obj: number, count: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p2(obj);
buf.p2(Math.min(count, 65536));
},
[ServerProt.MAP_ANIM]: (buf: Packet, srcX: number, srcZ: number,
spotanim: number, height: number, delay: number) =>
{
[ServerProt.MAP_ANIM]: (buf: Packet, srcX: number, srcZ: number, spotanim: number, height: number, delay: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p2(spotanim);
buf.p1(height);
buf.p2(delay);
},
[ServerProt.OBJ_ADD]: (buf: Packet, srcX: number, srcZ: number,
obj: number, count: number) =>
{
[ServerProt.OBJ_ADD]: (buf: Packet, srcX: number, srcZ: number, obj: number, count: number) => {
buf.p1(((srcX & 0x7) << 4) | (srcZ & 0x7));
buf.p2(obj);
buf.p2(Math.min(count, 65536));

View file

@ -24,8 +24,8 @@ export default class TcpServer {
const socket = new ClientSocket(s, ip, ClientSocket.TCP);
const seed = Packet.alloc(8);
seed.p4(Math.floor(Math.random() * 0xFFFFFFFF));
seed.p4(Math.floor(Math.random() * 0xFFFFFFFF));
seed.p4(Math.floor(Math.random() * 0xffffffff));
seed.p4(Math.floor(Math.random() * 0xffffffff));
socket.send(seed.data);
s.on('data', (_data: Buffer) => {

View file

@ -27,8 +27,8 @@ export default class TcpServer {
const socket = new ClientSocket(s, ip, ClientSocket.TCP);
const seed = Packet.alloc(8);
seed.p4(Math.floor(Math.random() * 0xFFFFFFFF));
seed.p4(Math.floor(Math.random() * 0xFFFFFFFF));
seed.p4(Math.floor(Math.random() * 0xffffffff));
seed.p4(Math.floor(Math.random() * 0xffffffff));
socket.send(seed.data);
s.on('data', async (data: Buffer) => {

View file

@ -26,7 +26,7 @@ export default class WSServer {
wss: WebSocketServer | null = null;
start() {
const port = ((Environment.GAME_PORT as number) + 1);
const port = (Environment.GAME_PORT as number) + 1;
this.wss = new WebSocketServer({ port, host: '0.0.0.0' }, () => {
console.log(`[WSMaintenance]: Listening on port ${port}`);
@ -39,8 +39,8 @@ export default class WSServer {
const socket = new ClientSocket(ws, ip, ClientSocket.WEBSOCKET);
const seed = Packet.alloc(8);
seed.p4(Math.floor(Math.random() * 0xFFFFFFFF));
seed.p4(Math.floor(Math.random() * 0xFFFFFFFF));
seed.p4(Math.floor(Math.random() * 0xffffffff));
seed.p4(Math.floor(Math.random() * 0xffffffff));
socket.send(seed.data);
ws.on('message', (data: any) => {

View file

@ -29,7 +29,7 @@ export default class WSServer {
wss: WebSocketServer | null = null;
start() {
const port = ((Environment.GAME_PORT as number) + 1);
const port = (Environment.GAME_PORT as number) + 1;
this.wss = new WebSocketServer({ port, host: '0.0.0.0' }, () => {
console.log(`[WSWorld]: Listening on port ${port}`);
@ -42,8 +42,8 @@ export default class WSServer {
const socket = new ClientSocket(ws, ip, ClientSocket.WEBSOCKET);
const seed = Packet.alloc(8);
seed.p4(Math.floor(Math.random() * 0xFFFFFFFF));
seed.p4(Math.floor(Math.random() * 0xFFFFFFFF));
seed.p4(Math.floor(Math.random() * 0xffffffff));
seed.p4(Math.floor(Math.random() * 0xffffffff));
socket.send(seed.data);
ws.on('message', async (data: Buffer) => {

View file

@ -11,13 +11,7 @@ if (args.length < 2) {
const inputDir = args[0];
const outputDir = args[1];
const names = [
['', 'title', 'config', 'interface', 'media', 'versionlist', 'textures', 'wordenc', 'sounds'],
[],
[],
[],
[]
];
const names = [['', 'title', 'config', 'interface', 'media', 'versionlist', 'textures', 'wordenc', 'sounds'], [], [], [], []];
const extensions = ['jag', 'dat', 'dat', 'mid', 'dat'];
const store = new JagStore(inputDir);

View file

@ -7,24 +7,60 @@ console.log('Packing media.jag');
//console.time('media.jag');
const jagOrder = [
'combatboxes.dat', 'staticons.dat', 'gnomeball_buttons.dat',
'miscgraphics2.dat', 'miscgraphics3.dat', 'backleft1.dat',
'backleft2.dat', 'tradebacking.dat', 'steelborder.dat',
'prayeron.dat', 'mapflag.dat', 'compass.dat',
'mapback.dat', 'headicons.dat', 'mapscene.dat',
'staticons2.dat', 'cross.dat', 'magicoff.dat',
'magicon2.dat', 'miscgraphics.dat', 'index.dat',
'magicon.dat', 'combaticons.dat', 'mapdots.dat',
'backtop1.dat', 'backtop2.dat', 'chatback.dat',
'backbase1.dat', 'backbase2.dat', 'hitmarks.dat',
'sideicons.dat', 'scrollbar.dat', 'prayerglow.dat',
'redstone1.dat', 'redstone2.dat', 'redstone3.dat',
'backhmid1.dat', 'backhmid2.dat', 'combaticons2.dat',
'combaticons3.dat', 'backvmid1.dat', 'backvmid2.dat',
'backvmid3.dat', 'wornicons.dat', 'sworddecor.dat',
'invback.dat', 'leftarrow.dat', 'magicoff2.dat',
'mapfunction.dat', 'prayeroff.dat', 'steelborder2.dat',
'rightarrow.dat', 'backright1.dat', 'backright2.dat'
'combatboxes.dat',
'staticons.dat',
'gnomeball_buttons.dat',
'miscgraphics2.dat',
'miscgraphics3.dat',
'backleft1.dat',
'backleft2.dat',
'tradebacking.dat',
'steelborder.dat',
'prayeron.dat',
'mapflag.dat',
'compass.dat',
'mapback.dat',
'headicons.dat',
'mapscene.dat',
'staticons2.dat',
'cross.dat',
'magicoff.dat',
'magicon2.dat',
'miscgraphics.dat',
'index.dat',
'magicon.dat',
'combaticons.dat',
'mapdots.dat',
'backtop1.dat',
'backtop2.dat',
'chatback.dat',
'backbase1.dat',
'backbase2.dat',
'hitmarks.dat',
'sideicons.dat',
'scrollbar.dat',
'prayerglow.dat',
'redstone1.dat',
'redstone2.dat',
'redstone3.dat',
'backhmid1.dat',
'backhmid2.dat',
'combaticons2.dat',
'combaticons3.dat',
'backvmid1.dat',
'backvmid2.dat',
'backvmid3.dat',
'wornicons.dat',
'sworddecor.dat',
'invback.dat',
'leftarrow.dat',
'magicoff2.dat',
'mapfunction.dat',
'prayeroff.dat',
'steelborder2.dat',
'rightarrow.dat',
'backright1.dat',
'backright2.dat'
];
const indexOrder = [
@ -80,7 +116,7 @@ const indexOrder = [
'magicon2.dat', // 6377
'magicoff2.dat', // 6750
'gnomeball_buttons.dat', // 7117
'mapflag.dat', // 7440
'mapflag.dat' // 7440
];
const files: Record<string, Packet> = {};

View file

@ -56,7 +56,7 @@ for (let i = 0; i < media.fileCount; i++) {
// ----
const pal = new Jimp(16, 16, 0xFF00FFFF).colorType(2);
const pal = new Jimp(16, 16, 0xff00ffff).colorType(2);
for (let j = 1; j < pix.palette.length; j++) {
const x = j % 16;
@ -64,10 +64,10 @@ for (let i = 0; i < media.fileCount; i++) {
const color = pix.palette[j];
const pos = (x + (y * 16)) * 4;
pal.bitmap.data[pos] = (color >> 16) & 0xFF;
pal.bitmap.data[pos + 1] = (color >> 8) & 0xFF;
pal.bitmap.data[pos + 2] = color & 0xFF;
const pos = (x + y * 16) * 4;
pal.bitmap.data[pos] = (color >> 16) & 0xff;
pal.bitmap.data[pos + 1] = (color >> 8) & 0xff;
pal.bitmap.data[pos + 2] = color & 0xff;
}
await pal.writeAsync(`dump/src/sprites/meta/${safeName}.pal.png`);
@ -81,8 +81,8 @@ for (let i = 0; i < media.fileCount; i++) {
let width = Math.ceil(Math.sqrt(count));
let height = Math.ceil(count / width);
const override: Record<string, { width: number, height: number }> = {
'mapdots': { width: 4, height: 1 }
const override: Record<string, { width: number; height: number }> = {
mapdots: { width: 4, height: 1 }
};
if (override[safeName]) {
@ -91,7 +91,8 @@ for (let i = 0; i < media.fileCount; i++) {
} else if (isPrime(count)) {
width = count;
height = 1;
} if (width * height != count) {
}
if (width * height != count) {
let heightTries = 0;
// wrong aspect ratio, try subtracting from height and adding to width
@ -127,7 +128,7 @@ for (let i = 0; i < media.fileCount; i++) {
console.log('Could not determine size of spritesheet', safeName, width, height, count);
}
const sheet = new Jimp(width * size.width, height * size.height, 0xFF00FFFF).colorType(2);
const sheet = new Jimp(width * size.width, height * size.height, 0xff00ffff).colorType(2);
for (let j = 0; j < count; j++) {
const x = j % width;
@ -151,7 +152,7 @@ for (let i = 0; i < media.fileCount; i++) {
// ----
const pal = new Jimp(16, 16, 0xFF00FFFF).colorType(2);
const pal = new Jimp(16, 16, 0xff00ffff).colorType(2);
for (let j = 1; j < sprites[0].palette.length; j++) {
const x = j % 16;
@ -159,10 +160,10 @@ for (let i = 0; i < media.fileCount; i++) {
const color = sprites[0].palette[j];
const pos = (x + (y * 16)) * 4;
pal.bitmap.data[pos] = (color >> 16) & 0xFF;
pal.bitmap.data[pos + 1] = (color >> 8) & 0xFF;
pal.bitmap.data[pos + 2] = color & 0xFF;
const pos = (x + y * 16) * 4;
pal.bitmap.data[pos] = (color >> 16) & 0xff;
pal.bitmap.data[pos + 1] = (color >> 8) & 0xff;
pal.bitmap.data[pos + 2] = color & 0xff;
}
await pal.writeAsync(`dump/src/sprites/meta/${safeName}.pal.png`);

View file

@ -292,7 +292,7 @@ export default class Model {
model.vertexLabel = [];
}
if (meta.faceInfosOffset >= 0) {
if (meta.faceInfosOffset >= 0) {
model.faceInfo = [];
}

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