Merge branch 'master' into 10-implement-money

This commit is contained in:
Johannes Loher 2021-01-18 19:48:07 +01:00
commit e89c87f0c1
46 changed files with 1651 additions and 505 deletions

View file

@ -1,4 +1,4 @@
Copyright 2020 Johannes Loher, Gesina Schwalbe, Oliver Rümpelein
Copyright 2020 Johannes Loher, Gesina Schwalbe, Oliver Rümpelein, Siegfried Krug
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

229
package-lock.json generated
View file

@ -44,9 +44,9 @@
}
},
"@eslint/eslintrc": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz",
"integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==",
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
"integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
@ -56,7 +56,7 @@
"ignore": "^4.0.6",
"import-fresh": "^3.2.1",
"js-yaml": "^3.13.1",
"lodash": "^4.17.19",
"lodash": "^4.17.20",
"minimatch": "^3.0.4",
"strip-json-comments": "^3.1.1"
},
@ -110,6 +110,15 @@
"fastq": "^1.6.0"
}
},
"@types/fs-extra": {
"version": "9.0.6",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.6.tgz",
"integrity": "sha512-ecNRHw4clCkowNOBJH1e77nvbPxHYnWIXMv1IAoG/9+MYGkgoyr3Ppxr7XYFNL41V422EDhyV4/4SSK8L2mlig==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/jasmine": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.2.tgz",
@ -131,6 +140,12 @@
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
"@types/node": {
"version": "14.14.20",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz",
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==",
"dev": true
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -144,9 +159,9 @@
"dev": true
},
"@types/socket.io-client": {
"version": "1.4.34",
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.34.tgz",
"integrity": "sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg==",
"version": "1.4.35",
"resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.35.tgz",
"integrity": "sha512-MI8YmxFS+jMkIziycT5ickBWK1sZwDwy16mgH/j99Mcom6zRG/NimNGQ3vJV0uX5G6g/hEw0FG3w3b3sT5OUGw==",
"dev": true
},
"@types/tinymce": {
@ -159,15 +174,16 @@
}
},
"@typescript-eslint/eslint-plugin": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.1.tgz",
"integrity": "sha512-fABclAX2QIEDmTMk6Yd7Muv1CzFLwWM4505nETzRHpP3br6jfahD9UUJkhnJ/g2m7lwfz8IlswcwGGPGiq9exw==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz",
"integrity": "sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "4.11.1",
"@typescript-eslint/scope-manager": "4.11.1",
"@typescript-eslint/experimental-utils": "4.14.0",
"@typescript-eslint/scope-manager": "4.14.0",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15",
"regexpp": "^3.0.0",
"semver": "^7.3.2",
"tsutils": "^3.17.1"
@ -215,28 +231,28 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.11.1.tgz",
"integrity": "sha512-mAlWowT4A6h0TC9F+J5pdbEhjNiEMO+kqPKQ4sc3fVieKL71dEqfkKgtcFVSX3cjSBwYwhImaQ/mXQF0oaI38g==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz",
"integrity": "sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.11.1",
"@typescript-eslint/types": "4.11.1",
"@typescript-eslint/typescript-estree": "4.11.1",
"@typescript-eslint/scope-manager": "4.14.0",
"@typescript-eslint/types": "4.14.0",
"@typescript-eslint/typescript-estree": "4.14.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
}
},
"@typescript-eslint/parser": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.11.1.tgz",
"integrity": "sha512-BJ3jwPQu1jeynJ5BrjLuGfK/UJu6uwHxJ/di7sanqmUmxzmyIcd3vz58PMR7wpi8k3iWq2Q11KMYgZbUpRoIPw==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.0.tgz",
"integrity": "sha512-sUDeuCjBU+ZF3Lzw0hphTyScmDDJ5QVkyE21pRoBo8iDl7WBtVFS+WDN3blY1CH3SBt7EmYCw6wfmJjF0l/uYg==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "4.11.1",
"@typescript-eslint/types": "4.11.1",
"@typescript-eslint/typescript-estree": "4.11.1",
"@typescript-eslint/scope-manager": "4.14.0",
"@typescript-eslint/types": "4.14.0",
"@typescript-eslint/typescript-estree": "4.14.0",
"debug": "^4.1.1"
},
"dependencies": {
@ -258,29 +274,29 @@
}
},
"@typescript-eslint/scope-manager": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.11.1.tgz",
"integrity": "sha512-Al2P394dx+kXCl61fhrrZ1FTI7qsRDIUiVSuN6rTwss6lUn8uVO2+nnF4AvO0ug8vMsy3ShkbxLu/uWZdTtJMQ==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz",
"integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.11.1",
"@typescript-eslint/visitor-keys": "4.11.1"
"@typescript-eslint/types": "4.14.0",
"@typescript-eslint/visitor-keys": "4.14.0"
}
},
"@typescript-eslint/types": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.11.1.tgz",
"integrity": "sha512-5kvd38wZpqGY4yP/6W3qhYX6Hz0NwUbijVsX2rxczpY6OXaMxh0+5E5uLJKVFwaBM7PJe1wnMym85NfKYIh6CA==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz",
"integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.1.tgz",
"integrity": "sha512-tC7MKZIMRTYxQhrVAFoJq/DlRwv1bnqA4/S2r3+HuHibqvbrPcyf858lNzU7bFmy4mLeIHFYr34ar/1KumwyRw==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz",
"integrity": "sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.11.1",
"@typescript-eslint/visitor-keys": "4.11.1",
"@typescript-eslint/types": "4.14.0",
"@typescript-eslint/visitor-keys": "4.14.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
@ -331,12 +347,12 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.1.tgz",
"integrity": "sha512-IrlBhD9bm4bdYcS8xpWarazkKXlE7iYb1HzRuyBP114mIaj5DJPo11Us1HgH60dTt41TCZXMaTCAW+OILIYPOg==",
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz",
"integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.11.1",
"@typescript-eslint/types": "4.14.0",
"eslint-visitor-keys": "^2.0.0"
}
},
@ -555,9 +571,9 @@
"dev": true
},
"archiver": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.1.0.tgz",
"integrity": "sha512-iKuQUP1nuKzBC2PFlGet5twENzCfyODmvkxwDV0cEFXavwcLrIW5ssTuHi9dyTPvpWr6Faweo2eQaQiLIwyXTA==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.2.0.tgz",
"integrity": "sha512-QEAKlgQuAtUxKeZB9w5/ggKXh21bZS+dzzuQ0RPBC20qtDCbTyzqmisoeJP46MP39fg4B4IcyvR+yeyEBdblsQ==",
"dev": true,
"requires": {
"archiver-utils": "^2.1.0",
@ -1954,13 +1970,13 @@
"dev": true
},
"eslint": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz",
"integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==",
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz",
"integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.2.2",
"@eslint/eslintrc": "^0.3.0",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -1984,7 +2000,7 @@
"js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash": "^4.17.19",
"lodash": "^4.17.20",
"minimatch": "^3.0.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
@ -2096,9 +2112,9 @@
"dev": true
},
"eslint-plugin-prettier": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.0.tgz",
"integrity": "sha512-tMTwO8iUWlSRZIwS9k7/E4vrTsfvsrcM5p1eftyuqWH25nKsz/o6/54I7jwQ/3zobISyC7wMy9ZsFwgTxOcOpQ==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz",
"integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==",
"dev": true,
"requires": {
"prettier-linter-helpers": "^1.0.0"
@ -2437,9 +2453,9 @@
"dev": true
},
"fast-glob": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
"integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
"integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
"dev": true,
"requires": {
"@nodelib/fs.stat": "^2.0.2",
@ -2584,12 +2600,12 @@
}
},
"find-versions": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz",
"integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz",
"integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==",
"dev": true,
"requires": {
"semver-regex": "^2.0.0"
"semver-regex": "^3.1.2"
}
},
"findup-sync": {
@ -2702,7 +2718,7 @@
}
},
"foundry-pc-types": {
"version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f84074f63d1aeeb9229e441e8c3ccaa9cba64142",
"version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#5fcca4e4327b558d5eeeb962f05470c994a394be",
"from": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
"dev": true,
"requires": {
@ -2970,9 +2986,9 @@
}
},
"globby": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
"integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz",
"integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==",
"dev": true,
"requires": {
"array-union": "^2.1.0",
@ -3378,18 +3394,18 @@
"dev": true
},
"husky": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/husky/-/husky-4.3.6.tgz",
"integrity": "sha512-o6UjVI8xtlWRL5395iWq9LKDyp/9TE7XMOTvIpEVzW638UcGxTmV5cfel6fsk/jbZSTlvfGVJf2svFtybcIZag==",
"version": "4.3.8",
"resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz",
"integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==",
"dev": true,
"requires": {
"chalk": "^4.0.0",
"ci-info": "^2.0.0",
"compare-versions": "^3.6.0",
"cosmiconfig": "^7.0.0",
"find-versions": "^3.2.0",
"find-versions": "^4.0.0",
"opencollective-postinstall": "^2.0.2",
"pkg-dir": "^4.2.0",
"pkg-dir": "^5.0.0",
"please-upgrade-node": "^3.2.0",
"slash": "^3.0.0",
"which-pm-runs": "^1.0.0"
@ -5155,40 +5171,49 @@
}
},
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz",
"integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==",
"dev": true,
"requires": {
"find-up": "^4.0.0"
"find-up": "^5.0.0"
},
"dependencies": {
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"requires": {
"locate-path": "^5.0.0",
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"requires": {
"p-locate": "^4.1.0"
"p-locate": "^5.0.0"
}
},
"p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"requires": {
"yocto-queue": "^0.1.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"requires": {
"p-limit": "^2.2.0"
"p-limit": "^3.0.2"
}
},
"path-exists": {
@ -5648,9 +5673,9 @@
"dev": true
},
"sass": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.32.0.tgz",
"integrity": "sha512-fhyqEbMIycQA4blrz/C0pYhv2o4x2y6FYYAH0CshBw3DXh5D5wyERgxw0ptdau1orc/GhNrhF7DFN2etyOCEng==",
"version": "1.32.4",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.32.4.tgz",
"integrity": "sha512-N0BT0PI/t3+gD8jKa83zJJUb7ssfQnRRfqN+GIErokW6U4guBpfYl8qYB+OFLEho+QvnV5ZH1R9qhUC/Z2Ch9w==",
"dev": true,
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
@ -5834,9 +5859,9 @@
}
},
"semver-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz",
"integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.2.tgz",
"integrity": "sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA==",
"dev": true
},
"set-blocking": {
@ -6391,9 +6416,9 @@
}
},
"table": {
"version": "6.0.6",
"resolved": "https://registry.npmjs.org/table/-/table-6.0.6.tgz",
"integrity": "sha512-OInCtPmDNieVBkVFi6C8RwU2S2H0h8mF3e3TQK4nreaUNCpooQUkI+A/KuEkm5FawfhWIfNqG+qfelVVR+V00g==",
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz",
"integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==",
"dev": true,
"requires": {
"ajv": "^7.0.2",
@ -6472,9 +6497,9 @@
}
},
"tar-stream": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz",
"integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dev": true,
"requires": {
"bl": "^4.0.3",
@ -6642,9 +6667,9 @@
"dev": true
},
"tsutils": {
"version": "3.17.1",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
"integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
"version": "3.19.1",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.19.1.tgz",
"integrity": "sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
@ -7235,6 +7260,12 @@
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
},
"yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true
},
"zip-stream": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.0.4.tgz",

View file

@ -24,6 +24,10 @@
{
"name": "Oliver Rümpelein",
"email": "foundryvtt@pheerai.de"
},
{
"name": "Siegfried Krug",
"email": "foundryvtt@asdil1991.de"
}
],
"scripts": {
@ -40,14 +44,15 @@
"format": "prettier --write 'src/**/*.(ts|json|scss)'"
},
"devDependencies": {
"@types/fs-extra": "^9.0.6",
"@types/jasmine": "^3.6.2",
"@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.1",
"archiver": "^5.1.0",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
"archiver": "^5.2.0",
"chalk": "^4.1.0",
"eslint": "^7.17.0",
"eslint": "^7.18.0",
"eslint-config-prettier": "^7.1.0",
"eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-prettier": "^3.3.1",
"foundry-pc-types": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
"fs-extra": "^9.0.1",
"gulp": "^4.0.2",
@ -55,13 +60,13 @@
"gulp-less": "^4.0.1",
"gulp-sass": "^4.1.0",
"gulp-typescript": "^6.0.0-alpha.1",
"husky": "^4.3.6",
"husky": "^4.3.8",
"jasmine": "^3.6.3",
"jasmine-xml-reporter": "^1.2.1",
"json-stringify-pretty-compact": "^2.0.0",
"lint-staged": "^10.5.3",
"prettier": "^2.2.1",
"sass": "^1.32.0",
"sass": "^1.32.4",
"ts-node": "^9.1.1",
"typescript": "^4.1.3",
"yargs": "^16.2.0"

View file

@ -65,37 +65,37 @@ describe("DS4 Rolls with one die and slaying dice, followup throw.", () => {
describe("DS4 Rolls with one die and crit roll modifications.", () => {
it("Should do a crit success on `1`.", () => {
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [1])).toEqual(
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [1])).toEqual(
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [1]),
);
});
it("Should do a crit success on `maxCritSucc`.", () => {
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [2])).toEqual(
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [2])).toEqual(
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [2]),
);
});
it("Should do a success on lower edge case `3`.", () => {
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [3])).toEqual(
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [3])).toEqual(
new RollResult(3, RollResultStatus.SUCCESS, [3]),
);
});
it("Should do a success on upper edge case `18`.", () => {
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [18])).toEqual(
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [18])).toEqual(
new RollResult(0, RollResultStatus.FAILURE, [18]),
);
});
it("Should do a crit fail on `minCritFail`.", () => {
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [19])).toEqual(
it("Should do a crit fail on `minCritFailure`.", () => {
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [19])).toEqual(
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19]),
);
});
it("Should do a crit fail on `20`", () => {
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFail: 19 }, [20])).toEqual(
expect(rollCheckSingleDie(4, { maxCritSuccess: 2, minCritFailure: 19 }, [20])).toEqual(
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]),
);
});
@ -171,37 +171,37 @@ describe("DS4 Rolls with multiple dice and no modifiers.", () => {
describe("DS4 Rolls with multiple dice and min/max modifiers.", () => {
it("Should do a crit fail on `19` for first roll.", () => {
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [19, 15, 6])).toEqual(
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [19, 15, 6])).toEqual(
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]),
);
});
it("Should succeed with all rolls crit successes (1 and 2).", () => {
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [2, 1, 2])).toEqual(
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [2, 1, 2])).toEqual(
new RollResult(48, RollResultStatus.CRITICAL_SUCCESS, [2, 1, 2]),
);
});
it("Should succeed with the last roll not being sufficient.", () => {
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [15, 15, 15])).toEqual(
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [15, 15, 15])).toEqual(
new RollResult(30, RollResultStatus.SUCCESS, [15, 15, 15]),
);
});
it("Should succeed with the last roll a crit success `2`.", () => {
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [15, 15, 2])).toEqual(
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [15, 15, 2])).toEqual(
new RollResult(38, RollResultStatus.SUCCESS, [15, 15, 2]),
);
});
it("Should succeed with the last roll being `20` and one crit success '2'.", () => {
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [15, 2, 20])).toEqual(
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [15, 2, 20])).toEqual(
new RollResult(43, RollResultStatus.SUCCESS, [15, 2, 20]),
);
});
it("Should succeed with the last roll being `19` and one crit success '2'.", () => {
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFail: 19 }, [15, 2, 19])).toEqual(
expect(rollCheckMultipleDice(48, { maxCritSuccess: 2, minCritFailure: 19 }, [15, 2, 19])).toEqual(
new RollResult(42, RollResultStatus.SUCCESS, [15, 2, 19]),
);
});
@ -209,7 +209,7 @@ describe("DS4 Rolls with multiple dice and min/max modifiers.", () => {
describe("DS4 Rolls with multiple dice and fail modifiers.", () => {
it("Should do a crit fail on `19` for first roll.", () => {
expect(rollCheckMultipleDice(48, { minCritFail: 19 }, [19, 15, 6])).toEqual(
expect(rollCheckMultipleDice(48, { minCritFailure: 19 }, [19, 15, 6])).toEqual(
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]),
);
});

View file

@ -0,0 +1,15 @@
import "jasmine";
import * as fs from "fs-extra";
import * as path from "path";
describe("English and german localization files", () => {
const localizationPath = "./src/lang/";
const en: Record<string, unknown> = fs.readJSONSync(path.join(localizationPath, "en.json"));
const de: Record<string, unknown> = fs.readJSONSync(path.join(localizationPath, "de.json"));
it("should have the same keys.", () => {
const deKeys = Object.keys(de);
const enKeys = Object.keys(en);
expect(deKeys).toEqual(enKeys);
});
});

View file

@ -1,12 +1,17 @@
{
"DS4.UserInteractionAddItem": "Neu",
"DS4.UserInteractionEditItem": "Bearbeiten",
"DS4.UserInteractionDeleteItem": "Löschen",
"DS4.NotOwned": "Nicht besessen",
"DS4.HeadingDescription": "Beschreibung",
"DS4.HeadingBiography": "Biografie",
"DS4.HeadingDetails": "Details",
"DS4.HeadingEffects": "Effekte",
"DS4.HeadingInventory": "Inventar",
"DS4.HeadingProfile": "Profil",
"DS4.HeadingTalents": "Talente & Fähigkeiten",
"DS4.HeadingTalentsAbilities": "Talente & Fähigkeiten",
"DS4.HeadingSpells": "Zaubersprüche",
"DS4.HeadingDescription": "Beschreibung",
"DS4.HeadingSpecialCreatureAbilites": "Besondere Fähigkeiten",
"DS4.AttackType": "Angriffs Typ",
"DS4.AttackTypeAbbr": "AT",
"DS4.WeaponBonus": "Waffen Bonus",
@ -16,11 +21,13 @@
"DS4.AttackTypeMelee": "Schlagen",
"DS4.AttackTypeRanged": "Schießen",
"DS4.AttackTypeMeleeRanged": "Schlagen + Schießen",
"DS4.Description": "Beschreibung",
"DS4.Quantity": "Menge",
"DS4.PriceGold": "Preis (Gold)",
"DS4.StorageLocation": "Wo gelagert",
"DS4.ItemEquipped": "Ausgerüstet",
"DS4.ItemOwner": "Eigentümer",
"DS4.ItemEquippedAbbr": "A",
"DS4.ItemOwner": "Besitzer",
"DS4.ItemAvailability": "Verfügbarkeit",
"DS4.ItemAvailabilityHamlet": "Dorf",
"DS4.ItemAvailabilityVilage": "Kleinstadt",
@ -36,6 +43,8 @@
"DS4.ItemTypeArmorPlural": "Panzerungen",
"DS4.ItemTypeShield": "Schild",
"DS4.ItemTypeShieldPlural": "Schilde",
"DS4.ItemTypeSpell": "Zauberspruch",
"DS4.ItemTypeSpellPlural": "Zaubersprüche",
"DS4.ItemTypeTrinket": "Schmuckstück",
"DS4.ItemTypeTrinketPlural": "Schmuckstücke",
"DS4.ItemTypeEquipment": "Ausrüstung",
@ -48,6 +57,8 @@
"DS4.ItemTypeLanguagePlural": "Sprachen",
"DS4.ItemTypeAlphabet": "Schriftzeichen",
"DS4.ItemTypeAlphabetPlural": "Schriftzeichen",
"DS4.ItemTypeSpecialCreatureAbility": "Besondere Kreaturenfähigkeit",
"DS4.ItemTypeSpecialCreatureAbilityPlural": "Besondere Kreaturenfähigkeiten",
"DS4.ArmorType": "Panzerungstyp",
"DS4.ArmorTypeAbbr": "PAT",
"DS4.ArmorMaterialType": "Material Typ",
@ -72,6 +83,29 @@
"DS4.ArmorMaterialTypeChainAbbr": "Ketten",
"DS4.ArmorMaterialTypePlate": "Platten",
"DS4.ArmorMaterialTypePlateAbbr": "Platten",
"DS4.SpellType": "Zauberspruchtyp",
"DS4.SpellTypeAbbr": "T",
"DS4.SpellTypeSpellcasting": "Zaubern",
"DS4.SpellTypeTargetedSpellcasting": "Zielzaubern",
"DS4.SpellCategory": "Kategorie",
"DS4.SpellCategoryHealing": "Heilung",
"DS4.SpellCategoryFire": "Feuer",
"DS4.SpellCategoryIce": "Eis",
"DS4.SpellCategoryLight": "Licht",
"DS4.SpellCategoryDarkness": "Schatten",
"DS4.SpellCategoryMindAffecting": "Geistensbeeinflussend",
"DS4.SpellCategoryElectricity": "Elektrizität",
"DS4.SpellCategoryNone": "Keine",
"DS4.SpellCategoryUnset": "Nicht gesetzt",
"DS4.SpellBonus": "Zauberbonus",
"DS4.SpellBonusAbbr": "ZB",
"DS4.SpellMaxDistance": "Reichweite",
"DS4.SpellEffectRadius": "Effektradius",
"DS4.SpellDuration": "Wirkdauer",
"DS4.SpellCooldownDuration": "Abklingzeit",
"DS4.SpellScrollPriceGold": "Schriftrollenpreis (Gold)",
"DS4.ActorTypeCharacter": "Charakter",
"DS4.ActorTypeCreature": "Kreatur",
"DS4.AttributeBody": "Körper",
"DS4.AttributeMobility": "Agilität",
"DS4.AttributeMind": "Geist",
@ -89,35 +123,83 @@
"DS4.CombatValuesRangedAttack": "Schießen",
"DS4.CombatValuesSpellcasting": "Zaubern",
"DS4.CombatValuesTargetedSpellcasting": "Zielzaubern",
"DS4.BaseInfoRace": "Volk",
"DS4.BaseInfoClass": "Klasse",
"DS4.BaseInfoHeroClass": "Helden Klasse",
"DS4.BaseInfoCulture": "Kultur",
"DS4.ProgressionLevel": "Stufe",
"DS4.ProgressionExperiencePoints": "Erfahrungspunkte",
"DS4.ProgressionTalentPoints": "Talentpunkte",
"DS4.ProgressionProgressPoints": "Lernpunkte",
"DS4.CharacterBaseInfoRace": "Volk",
"DS4.CharacterBaseInfoClass": "Klasse",
"DS4.CharacterBaseInfoHeroClass": "Helden Klasse",
"DS4.CharacterBaseInfoCulture": "Kultur",
"DS4.CharacterProgressionLevel": "Stufe",
"DS4.CharacterProgressionExperiencePoints": "Erfahrungspunkte",
"DS4.CharacterProgressionTalentPoints": "Talentpunkte",
"DS4.CharacterProgressionProgressPoints": "Lernpunkte",
"DS4.TalentRank": "Rang",
"DS4.TalentRankBase": "Erworbener Rang",
"DS4.TalentRankMax": "Maximaler Rang",
"DS4.TalentRankMod": "Zusätzlicher Rang",
"DS4.TalentRankTotal": "Gesamter Rang",
"DS4.LanguageLanguages": "Sprachen",
"DS4.LanguageAlphabets": "Schriftzeichen",
"DS4.ProfileGender": "Geschlecht",
"DS4.ProfileBirthday": "Geburtstag",
"DS4.ProfileBirthplace": "Geburtsort",
"DS4.ProfileAge": "Alter",
"DS4.ProfileHeight": "Größe",
"DS4.ProfilHairColor": "Haarfarbe",
"DS4.ProfileWeight": "Gewicht",
"DS4.ProfileEyeColor": "Augenfarbe",
"DS4.ProfileSpecialCharacteristics": "Besondere Eigenschaften",
"DS4.CurrencyGold": "Gold",
"DS4.CurrencySilver": "Silber",
"DS4.CurrencyCopper": "Kupfer",
"DS4.Currency": "Währung",
"DS4.CharacterLanguageLanguages": "Sprachen",
"DS4.CharacterLanguageAlphabets": "Schriftzeichen",
"DS4.SpecialCreatureAbilityExperiencePoints": "Erfahrungspunkte",
"DS4.CharacterProfileBiography": "Biographie",
"DS4.CharacterProfileGender": "Geschlecht",
"DS4.CharacterProfileBirthday": "Geburtstag",
"DS4.CharacterProfileBirthplace": "Geburtsort",
"DS4.CharacterProfileAge": "Alter",
"DS4.CharacterProfileHeight": "Größe",
"DS4.CharacterProfileHairColor": "Haarfarbe",
"DS4.CharacterProfileWeight": "Gewicht",
"DS4.CharacterProfileEyeColor": "Augenfarbe",
"DS4.CharacterProfileSpecialCharacteristics": "Besondere Eigenschaften",
"DS4.CharacterCurrencyGold": "Gold",
"DS4.CharacterCurrencySilver": "Silber",
"DS4.CharacterCurrencyCopper": "Kupfer",
"DS4.CharacterCurrency": "Währung",
"DS4.CreatureTypeAnimal": "Tier",
"DS4.CreatureTypeConstruct": "Konstrukt",
"DS4.CreatureTypeHumanoid": "Humanoid",
"DS4.CreatureTypeMagicalEntity": "Magisches Wesen",
"DS4.CreatureTypePlantBeing": "Pflanzenwesen",
"DS4.CreatureTypeUndead": "Untot",
"DS4.CreatureSizeCategoryTiny": "Winzig",
"DS4.CreatureSizeCategorySmall": "Klein",
"DS4.CreatureSizeCategoryNormal": "Normal",
"DS4.CreatureSizeCategoryLarge": "Groß",
"DS4.CreatureSizeCategoryHuge": "Riesig",
"DS4.CreatureSizeCategoryColossal": "Gewaltig",
"DS4.CreatureBaseInfoLoot": "Beute",
"DS4.CreatureBaseInfoFoeFactor": "Gegnerhärte",
"DS4.CreatureBaseInfoCreatureType": "Kreaturengruppe",
"DS4.CreatureBaseInfoSizeCategory": "Größenkategorie",
"DS4.CreatureBaseInfoExperiencePoints": "Erfahrungspunkte",
"DS4.CreatureBaseInfoDescription": "Beschreibung",
"DS4.WarningManageActiveEffectOnOwnedItem": "Das Verwalten von aktiven Effekten innerhalb eines besessen Items wird derzeit nicht unterstützt und wird in einem nachfolgenden Update hinzugefügt.",
"DS4.WarningActorCannotOwnItem": "Der Aktor '{actorName}' vom Typ '{actorType}' kann das Item '{itemName}' vom Typ '{itemType}' nicht besitzen.",
"DS4.ErrorDiceCritOverlap": "Es gibt eine Überlappung zwischen Patzern und Immersiegen.",
"DS4.ErrorExplodingRecursionLimitExceeded": "Die maximale Rekursionstiefe für slayende Würfelwürfe wurde überschritten."
"DS4.ErrorExplodingRecursionLimitExceeded": "Die maximale Rekursionstiefe für slayende Würfelwürfe wurde überschritten.",
"DS4.UnitRounds": "Runden",
"DS4.UnitRoundsAbbr": "Rnd",
"DS4.UnitMinutes": "Minuten",
"DS4.UnitMinutesAbbr": "min",
"DS4.UnitHours": "Stunden",
"DS4.UnitHoursAbbr": "h",
"DS4.UnitDays": "Tage",
"DS4.UnitDaysAbbr": "d",
"DS4.UnitMeters": "Meter",
"DS4.UnitMetersAbbr": "m",
"DS4.UnitKilometers": "Kilometer",
"DS4.UnitKilometersAbbr": "km",
"DS4.UnitCustom": "individuell",
"DS4.UnitCustomAbbr": " ",
"DS4.RollDialogDefaultTitle": "Proben-Optionen",
"DS4.RollDialogOkButton": "Ok",
"DS4.RollDialogCancelButton": "Abbrechen",
"DS4.ErrorUnexpectedHtmlType": "Typfehler: Erwartet wurde {exType}, tatsächlich erhalten wurde {realType}",
"DS4.RollDialogTargetLabel": "Probenwert",
"DS4.RollDialogModifierLabel": "SL-Modifikator",
"DS4.RollDialogCoupLabel": "Immersieg bis",
"DS4.RollDialogFumbleLabel": "Patzer ab",
"DS4.RollDialogVisibilityLabel": "Sichtbarkeit",
"DS4.ChatVisibilityRoll": "Alle",
"DS4.ChatVisibilityGmRoll": "Selbst & SL",
"DS4.ChatVisibilityBlindRoll": "Nur SL",
"DS4.ChatVisibilitySelfRoll": "Nur selbst"
}

View file

@ -1,12 +1,17 @@
{
"DS4.UserInteractionAddItem": "Add item",
"DS4.UserInteractionEditItem": "Edit item",
"DS4.UserInteractionDeleteItem": "Delete item",
"DS4.NotOwned": "No owner",
"DS4.HeadingDescription": "Description",
"DS4.HeadingBiography": "Biography",
"DS4.HeadingDetails": "Details",
"DS4.HeadingEffects": "Effects",
"DS4.HeadingInventory": "Inventory",
"DS4.HeadingProfile": "Profile",
"DS4.HeadingTalents": "Talents & Abilities",
"DS4.HeadingTalentsAbilities": "Talents & Abilities",
"DS4.HeadingSpells": "Spells",
"DS4.HeadingDescription": "Description",
"DS4.HeadingSpecialCreatureAbilites": "Special Abilites",
"DS4.AttackType": "Attack Type",
"DS4.AttackTypeAbbr": "AT",
"DS4.WeaponBonus": "Weapon Bonus",
@ -16,10 +21,12 @@
"DS4.AttackTypeMelee": "Melee",
"DS4.AttackTypeRanged": "Ranged",
"DS4.AttackTypeMeleeRanged": "Melee / Ranged",
"DS4.Description": "Description",
"DS4.Quantity": "Quantity",
"DS4.PriceGold": "Price (Gold)",
"DS4.StorageLocation": "Stored at",
"DS4.ItemEquipped": "Equipped",
"DS4.ItemEquippedAbbr": "E",
"DS4.ItemOwner": "Owner",
"DS4.ItemAvailability": "Availability",
"DS4.ItemAvailabilityHamlet": "Hamlet",
@ -36,6 +43,8 @@
"DS4.ItemTypeArmorPlural": "Armor",
"DS4.ItemTypeShield": "Shield",
"DS4.ItemTypeShieldPlural": "Shields",
"DS4.ItemTypeSpell": "Spell",
"DS4.ItemTypeSpellPlural": "Spells",
"DS4.ItemTypeTrinket": "Trinket",
"DS4.ItemTypeTrinketPlural": "Trinkets",
"DS4.ItemTypeEquipment": "Equipment",
@ -48,6 +57,8 @@
"DS4.ItemTypeLanguagePlural": "Languages",
"DS4.ItemTypeAlphabet": "Alphabet",
"DS4.ItemTypeAlphabetPlural": "Alphabets",
"DS4.ItemTypeSpecialCreatureAbility": "Special Creature Ability",
"DS4.ItemTypeSpecialCreatureAbilityPlural": "Special Creature Abilities",
"DS4.ArmorType": "Armor Type",
"DS4.ArmorTypeAbbr": "AT",
"DS4.ArmorMaterialType": "Material Type",
@ -72,6 +83,29 @@
"DS4.ArmorMaterialTypeChainAbbr": "Chain",
"DS4.ArmorMaterialTypePlate": "Plate",
"DS4.ArmorMaterialTypePlateAbbr": "Plate",
"DS4.SpellType": "Spell Type",
"DS4.SpellTypeAbbr": "T",
"DS4.SpellTypeSpellcasting": "Spellcasting",
"DS4.SpellTypeTargetedSpellcasting": "Targeted Spellcasting",
"DS4.SpellCategory": "Category",
"DS4.SpellCategoryHealing": "Healing",
"DS4.SpellCategoryFire": "Fire",
"DS4.SpellCategoryIce": "Ice",
"DS4.SpellCategoryLight": "Light",
"DS4.SpellCategoryDarkness": "Darkness",
"DS4.SpellCategoryMindAffecting": "Mind Affecting",
"DS4.SpellCategoryElectricity": "Electricity",
"DS4.SpellCategoryNone": "None",
"DS4.SpellCategoryUnset": "Unset",
"DS4.SpellBonus": "Spell Bonus",
"DS4.SpellBonusAbbr": "SB",
"DS4.SpellMaxDistance": "Range",
"DS4.SpellEffectRadius": "Radius",
"DS4.SpellDuration": "Duration",
"DS4.SpellCooldownDuration": "Cooldown",
"DS4.SpellScrollPriceGold": "Scroll Price (Gold)",
"DS4.ActorTypeCharacter": "Character",
"DS4.ActorTypeCreature": "Creature",
"DS4.AttributeBody": "Body",
"DS4.AttributeMobility": "Mobility",
"DS4.AttributeMind": "Mind",
@ -89,35 +123,83 @@
"DS4.CombatValuesRangedAttack": "Ranged Attack",
"DS4.CombatValuesSpellcasting": "Spellcasting",
"DS4.CombatValuesTargetedSpellcasting": "Targeted Spellcasting",
"DS4.BaseInfoRace": "Race",
"DS4.BaseInfoClass": "Class",
"DS4.BaseInfoHeroClass": "Hero Class",
"DS4.BaseInfoCulture": "Culture",
"DS4.ProgressionLevel": "Level",
"DS4.ProgressionExperiencePoints": "Experience Points",
"DS4.ProgressionTalentPoints": "Talent Points",
"DS4.ProgressionProgressPoints": "Progress Points",
"DS4.CharacterBaseInfoRace": "Race",
"DS4.CharacterBaseInfoClass": "Class",
"DS4.CharacterBaseInfoHeroClass": "Hero Class",
"DS4.CharacterBaseInfoCulture": "Culture",
"DS4.CharacterProgressionLevel": "Level",
"DS4.CharacterProgressionExperiencePoints": "Experience Points",
"DS4.CharacterProgressionTalentPoints": "Talent Points",
"DS4.CharacterProgressionProgressPoints": "Progress Points",
"DS4.TalentRank": "Rank",
"DS4.TalentRankBase": "Acquired Ranks",
"DS4.TalentRankMax": "Maximum Ranks",
"DS4.TalentRankMod": "Additional Ranks",
"DS4.TalentRankTotal": "Total Ranks",
"DS4.LanguageLanguages": "Languages",
"DS4.LanguageAlphabets": "Alphabets",
"DS4.ProfileGender": "Gender",
"DS4.ProfileBirthday": "Birthday",
"DS4.ProfileBirthplace": "Birthplace",
"DS4.ProfileAge": "Age",
"DS4.ProfileHeight": "Height",
"DS4.ProfilHairColor": "Hair Color",
"DS4.ProfileWeight": "Weight",
"DS4.ProfileEyeColor": "Eye Color",
"DS4.ProfileSpecialCharacteristics": "Special Characteristics",
"DS4.CurrencyGold": "Gold",
"DS4.CurrencySilver": "Silver",
"DS4.CurrencyCopper": "Copper",
"DS4.Currency": "Currency",
"DS4.CharacterLanguageLanguages": "Languages",
"DS4.CharacterLanguageAlphabets": "Alphabets",
"DS4.SpecialCreatureAbilityExperiencePoints": "Experience Points",
"DS4.CharacterProfileBiography": "Biography",
"DS4.CharacterProfileGender": "Gender",
"DS4.CharacterProfileBirthday": "Birthday",
"DS4.CharacterProfileBirthplace": "Birthplace",
"DS4.CharacterProfileAge": "Age",
"DS4.CharacterProfileHeight": "Height",
"DS4.CharacterProfileHairColor": "Hair Color",
"DS4.CharacterProfileWeight": "Weight",
"DS4.CharacterProfileEyeColor": "Eye Color",
"DS4.CharacterProfileSpecialCharacteristics": "Special Characteristics",
"DS4.CharacterCurrencyGold": "Gold",
"DS4.CharacterCurrencySilver": "Silver",
"DS4.CharacterCurrencyCopper": "Copper",
"DS4.CharacterCurrency": "Currency",
"DS4.CreatureTypeAnimal": "Animal",
"DS4.CreatureTypeConstruct": "Construct",
"DS4.CreatureTypeHumanoid": "Humanoid",
"DS4.CreatureTypeMagicalEntity": "Magical Entity",
"DS4.CreatureTypePlantBeing": "Plant Being",
"DS4.CreatureTypeUndead": "Undead",
"DS4.CreatureSizeCategoryTiny": "Tiny",
"DS4.CreatureSizeCategorySmall": "Small",
"DS4.CreatureSizeCategoryNormal": "Normal",
"DS4.CreatureSizeCategoryLarge": "Large",
"DS4.CreatureSizeCategoryHuge": "Huge",
"DS4.CreatureSizeCategoryColossal": "Colossal",
"DS4.CreatureBaseInfoLoot": "Loot",
"DS4.CreatureBaseInfoFoeFactor": "Foe Factor",
"DS4.CreatureBaseInfoCreatureType": "Creature Type",
"DS4.CreatureBaseInfoSizeCategory": "Size Category",
"DS4.CreatureBaseInfoExperiencePoints": "Experience Points",
"DS4.CreatureBaseInfoDescription": "Description",
"DS4.WarningManageActiveEffectOnOwnedItem": "Managing Active Effects within an Owned Item is not currently supported and will be added in a subsequent update.",
"DS4.WarningActorCannotOwnItem": "The actor '{actorName}' of type '{actorType}' cannot own the item '{itemName}' of type '{itemType}'.",
"DS4.ErrorDiceCritOverlap": "There's an overlap between Fumbles and Coups",
"DS4.ErrorExplodingRecursionLimitExceeded": "Maximum recursion depth for exploding dice roll exceeded"
"DS4.ErrorExplodingRecursionLimitExceeded": "Maximum recursion depth for exploding dice roll exceeded",
"DS4.UnitRounds": "Rounds",
"DS4.UnitRoundsAbbr": "rnd",
"DS4.UnitMinutes": "Minutes",
"DS4.UnitMinutesAbbr": "min",
"DS4.UnitHours": "Hours",
"DS4.UnitHoursAbbr": "h",
"DS4.UnitDays": "Days",
"DS4.UnitDaysAbbr": "d",
"DS4.UnitMeters": "Meters",
"DS4.UnitMetersAbbr": "m",
"DS4.UnitKilometers": "Kilometers",
"DS4.UnitKilometersAbbr": "km",
"DS4.UnitCustom": "Custom Unit",
"DS4.UnitCustomAbbr": " ",
"DS4.RollDialogDefaultTitle": "Roll Options",
"DS4.RollDialogOkButton": "Ok",
"DS4.RollDialogCancelButton": "Cancel",
"DS4.ErrorUnexpectedHtmlType": "Type Error: Expected {exType}, got {realType}",
"DS4.RollDialogTargetLabel": "Check Target Number",
"DS4.RollDialogModifierLabel": "Game Master Modifier",
"DS4.RollDialogCoupLabel": "Coup to",
"DS4.RollDialogFumbleLabel": "Fumble from",
"DS4.RollDialogVisibilityLabel": "Visibility",
"DS4.ChatVisibilityRoll": "All",
"DS4.ChatVisibilityGmRoll": "Self & GM",
"DS4.ChatVisibilityBlindRoll": "GM only",
"DS4.ChatVisibilitySelfRoll": "Self only"
}

View file

@ -1,39 +1,19 @@
export interface DS4ActorDataType {
import { ModifiableData, ResourceData, UsableResource } from "../common/common-data";
export type DS4ActorDataType = DS4ActorDataCharacter | DS4ActorDataCreature;
interface DS4ActorDataBase {
attributes: DS4ActorDataAttributes;
traits: DS4ActorDataTraits;
combatValues: DS4ActorDataCombatValues;
baseInfo: DS4ActorDataBaseInfo;
progression: DS4ActorDataProgression;
language: DS4ActorDataLanguage;
profile: DS4ActorDataProfile;
currency: DS4ActorDataCurrency;
}
interface DS4ActorDataAttributes {
body: BodyAttribute;
body: ModifiableData<number>;
mobility: ModifiableData<number>;
mind: ModifiableData<number>;
}
export interface ModifiableData<T> {
base: T;
mod: T;
total?: T;
}
interface UsableResource<T> {
total: T;
used: T;
}
interface ResourceData<T> extends ModifiableData<T> {
value: T;
max?: T;
}
// Blueprint in case we need more detailed differentiation
type BodyAttribute = ModifiableData<number>;
interface DS4ActorDataTraits {
strength: ModifiableData<number>;
constitution: ModifiableData<number>;
@ -54,26 +34,35 @@ interface DS4ActorDataCombatValues {
targetedSpellcasting: ModifiableData<number>;
}
interface DS4ActorDataBaseInfo {
interface DS4ActorDataCharacter extends DS4ActorDataBase {
baseInfo: DS4ActorDataCharacterBaseInfo;
progression: DS4ActorDataCharacterProgression;
language: DS4ActorDataCharacterLanguage;
profile: DS4ActorDataCharacterProfile;
currency: DS4ActorDataCharacterCurrency;
}
interface DS4ActorDataCharacterBaseInfo {
race: string;
class: string;
heroClass: string;
culture: string;
}
interface DS4ActorDataProgression {
interface DS4ActorDataCharacterProgression {
level: number;
experiencePoints: number;
talentPoints: UsableResource<number>;
progressPoints: UsableResource<number>;
}
interface DS4ActorDataLanguage {
interface DS4ActorDataCharacterLanguage {
languages: string;
alphabets: string;
}
interface DS4ActorDataProfile {
interface DS4ActorDataCharacterProfile {
biography: string;
gender: string;
birthday: string;
birthplace: string;
@ -85,8 +74,25 @@ interface DS4ActorDataProfile {
specialCharacteristics: string;
}
interface DS4ActorDataCurrency {
interface DS4ActorDataCharacterCurrency {
gold: number;
silver: number;
copper: number;
}
interface DS4ActorDataCreature extends DS4ActorDataBase {
baseInfo: DS4ActorDataCreatureBaseInfo;
}
type CreatureType = "animal" | "construct" | "humanoid" | "magicalEntity" | "plantBeing" | "undead";
type SizeCategory = "tiny" | "small" | "normal" | "large" | "huge" | "colossal";
interface DS4ActorDataCreatureBaseInfo {
loot: string;
foeFactor: number;
creatureType: CreatureType;
sizeCategory: SizeCategory;
experiencePoints: number;
description: string;
}

View file

@ -1,6 +1,7 @@
import { ModifiableData } from "../common/common-data";
import { DS4Item } from "../item/item";
import { DS4ItemDataType } from "../item/item-data";
import { DS4ActorDataType, ModifiableData } from "./actor-data";
import { DS4ItemDataType, ItemType } from "../item/item-data";
import { DS4ActorDataType } from "./actor-data";
export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item> {
/** @override */
@ -21,4 +22,37 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
combatValues.hitPoints.max = combatValues.hitPoints.total;
}
/**
* The list of item types that can be owned by this actor.
*/
get ownableItemTypes(): Array<ItemType> {
switch (this.data.type) {
case "character":
return [
"weapon",
"armor",
"shield",
"trinket",
"equipment",
"spell",
"talent",
"racialAbility",
"language",
"alphabet",
];
case "creature":
return ["weapon", "armor", "shield", "trinket", "equipment", "spell", "specialCreatureAbility"];
default:
return [];
}
}
/**
* Checks whether or not the given item type can be owned by the actor.
* @param itemType the item type to check
*/
canOwnItemType(itemType: ItemType): boolean {
return this.ownableItemTypes.includes(itemType);
}
}

View file

@ -1,12 +1,30 @@
import { DS4ItemDataType } from "../item/item-data";
import { DS4Actor } from "./actor";
import { DS4ActorDataType } from "./actor-data";
import { DS4Item } from "../../item/item";
import { DS4ItemDataType, ItemType } from "../../item/item-data";
import { DS4Actor } from "../actor";
import { DS4ActorDataType } from "../actor-data";
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4ItemDataType> {
/** @override */
static get defaultOptions(): FormApplicationOptions {
return mergeObject(super.defaultOptions, {
classes: ["ds4", "sheet", "actor"],
width: 745,
height: 600,
});
}
/** @override */
get template(): string {
const path = "systems/ds4/templates/actor";
return `${path}/${this.actor.data.type}-sheet.hbs`;
}
/* -------------------------------------------- */
/**
* This method returns the data for the template of the actor sheet.
* It explicitly adds the items of the object sorted by type in the
@ -21,21 +39,9 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
// Add the items explicitly sorted by type to the data:
itemsByType: this.actor.itemTypes,
};
console.log("Data:", data);
return data;
}
/** @override */
static get defaultOptions(): FormApplicationOptions {
return mergeObject(super.defaultOptions, {
classes: ["ds4", "sheet", "actor"],
template: "systems/ds4/templates/actor/actor-sheet.hbs",
width: 745,
height: 600,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
});
}
/* -------------------------------------------- */
/** @override */
@ -200,4 +206,26 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
});
}
}
/**
* @override
*/
async _onDrop(event: DragEvent): Promise<boolean | unknown> {
const data = JSON.parse(event.dataTransfer?.getData("text/plain")) as { type?: string };
if (data.type === "Item") {
const item = await Item.fromDropData(data as Parameters<typeof DS4Item.fromDropData>[0]);
if (item && !this.actor.canOwnItemType(item.data.type as ItemType)) {
ui.notifications.warn(
game.i18n.format("DS4.WarningActorCannotOwnItem", {
actorName: this.actor.name,
actorType: this.actor.data.type,
itemName: item.name,
itemType: item.data.type,
}),
);
return false;
}
}
return super._onDrop(event);
}
}

View file

@ -0,0 +1,11 @@
import { DS4ActorSheet } from "./actor-sheet";
export class DS4CharacterActorSheet extends DS4ActorSheet {
/** @override */
static get defaultOptions(): FormApplicationOptions {
return mergeObject(super.defaultOptions, {
classes: ["ds4", "sheet", "actor", "character"],
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "inventory" }],
});
}
}

View file

@ -0,0 +1,11 @@
import { DS4ActorSheet } from "./actor-sheet";
export class DS4CreatureActorSheet extends DS4ActorSheet {
/** @override */
static get defaultOptions(): FormApplicationOptions {
return mergeObject(super.defaultOptions, {
classes: ["ds4", "sheet", "actor", "creature"],
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "inventory" }],
});
}
}

View file

@ -0,0 +1,15 @@
export interface ModifiableData<T> {
base: T;
mod: T;
total?: T;
}
export interface ResourceData<T> extends ModifiableData<T> {
value: T;
max?: T;
}
export interface UsableResource<T> {
total: T;
used: T;
}

View file

@ -26,6 +26,14 @@ export const DS4 = {
ranged: "systems/ds4/assets/official/DS4-RAT.png",
},
/**
* Define the file paths to icon images
*/
spellTypesIcons: {
spellcasting: "systems/ds4/assets/official/DS4-SPC.png",
targetedSpellcasting: "systems/ds4/assets/official/DS4-TSC.png",
},
/**
* Define the set of item availabilties
*/
@ -46,12 +54,14 @@ export const DS4 = {
weapon: "DS4.ItemTypeWeapon",
armor: "DS4.ItemTypeArmor",
shield: "DS4.ItemTypeShield",
spell: "DS4.ItemTypeSpell",
trinket: "DS4.ItemTypeTrinket",
equipment: "DS4.ItemTypeEquipment",
talent: "DS4.ItemTypeTalent",
racialAbility: "DS4.ItemTypeRacialAbility",
language: "DS4.ItemTypeLanguage",
alphabet: "DS4.ItemTypeAlphabet",
specialCreatureAbility: "DS4.ItemTypeSpecialCreatureAbility",
},
/**
@ -96,8 +106,33 @@ export const DS4 = {
plate: "DS4.ArmorMaterialTypePlateAbbr",
},
spellTypes: {
spellcasting: "DS4.SpellTypeSpellcasting",
targetedSpellcasting: "DS4.SpellTypeTargetedSpellcasting",
},
spellCategories: {
healing: "DS4.SpellCategoryHealing",
fire: "DS4.SpellCategoryFire",
ice: "DS4.SpellCategoryIce",
light: "DS4.SpellCategoryLight",
darkness: "DS4.SpellCategoryDarkness",
mindAffecting: "DS4.SpellCategoryMindAffecting",
electricity: "DS4.SpellCategoryElectricity",
none: "DS4.SpellCategoryNone",
unset: "DS4.SpellCategoryUnset",
},
/**
* Define the set of attributes a character has
* Define the set of actor types
*/
actorTypes: {
character: "DS4.ActorTypeCharacter",
creature: "DS4.ActorTypeCreature",
},
/**
* Define the set of attributes an actor has
*/
attributes: {
body: "DS4.AttributeBody",
@ -106,7 +141,7 @@ export const DS4 = {
},
/**
* Define the set of traits a character has
* Define the set of traits an actor has
*/
traits: {
strength: "DS4.TraitStrength",
@ -118,7 +153,7 @@ export const DS4 = {
},
/**
* Define the set of combat values a character has
* Define the set of combat values an actor has
*/
combatValues: {
hitPoints: "DS4.CombatValuesHitPoints",
@ -134,50 +169,52 @@ export const DS4 = {
/**
* Define the base info of a character
*/
baseInfo: {
race: "DS4.BaseInfoRace",
class: "DS4.BaseInfoClass",
heroClass: "DS4.BaseInfoHeroClass",
culture: "DS4.BaseInfoCulture",
characterBaseInfo: {
race: "DS4.CharacterBaseInfoRace",
class: "DS4.CharacterBaseInfoClass",
heroClass: "DS4.CharacterBaseInfoHeroClass",
culture: "DS4.CharacterBaseInfoCulture",
},
/**
* Define the progression info of a character
*/
progression: {
level: "DS4.ProgressionLevel",
experiencePoints: "DS4.ProgressionExperiencePoints",
talentPoints: "DS4.ProgressionTalentPoints",
progressPoints: "DS4.ProgressionProgressPoints",
characterProgression: {
level: "DS4.CharacterProgressionLevel",
experiencePoints: "DS4.CharacterProgressionExperiencePoints",
talentPoints: "DS4.CharacterProgressionTalentPoints",
progressPoints: "DS4.CharacterProgressionProgressPoints",
},
/**
* Define the language info of a character
*/
language: {
languages: "DS4.LanguageLanguages",
alphabets: "DS4.LanguageAlphabets",
characterLanguage: {
languages: "DS4.CharacterLanguageLanguages",
alphabets: "DS4.CharacterLanguageAlphabets",
},
/**
* Define the profile info of a character
*/
profile: {
gender: "DS4.ProfileGender",
birthday: "DS4.ProfileBirthday",
birthplace: "DS4.ProfileBirthplace",
age: "DS4.ProfileAge",
height: "DS4.ProfileHeight",
hairColor: "DS4.ProfilHairColor",
weight: "DS4.ProfileWeight",
eyeColor: "DS4.ProfileEyeColor",
specialCharacteristics: "DS4.ProfileSpecialCharacteristics",
characterProfile: {
biography: "DS4.CharacterProfileBiography",
gender: "DS4.CharacterProfileGender",
birthday: "DS4.CharacterProfileBirthday",
birthplace: "DS4.CharacterProfileBirthplace",
age: "DS4.CharacterProfileAge",
height: "DS4.CharacterProfileHeight",
hairColor: "DS4.CharacterProfileHairColor",
weight: "DS4.CharacterProfileWeight",
eyeColor: "DS4.CharacterProfileEyeColor",
specialCharacteristics: "DS4.CharacterProfileSpecialCharacteristics",
},
/**
* Define the profile info types for hanndlebars of a character
* Define the profile info types for handlebars of a character
*/
profileDTypes: {
characterProfileDTypes: {
biography: "String",
gender: "String",
birthday: "String",
birthplace: "String",
@ -192,9 +229,94 @@ export const DS4 = {
/**
* Define currency elements of a character
*/
currency: {
gold: "DS4.CurrencyGold",
silver: "DS4.CurrencySilver",
copper: "DS4.CurrencyCopper",
characterCurrency: {
gold: "DS4.CharacterCurrencyGold",
silver: "DS4.CharacterCurrencySilver",
copper: "DS4.CharacterCurrencyCopper",
},
/**
* Define the different creature types a creature can be
*/
creatureTypes: {
animal: "DS4.CreatureTypeAnimal",
construct: "DS4.CreatureTypeConstruct",
humanoid: "DS4.CreatureTypeHumanoid",
magicalEntity: "DS4.CreatureTypeMagicalEntity",
plantBeing: "DS4.CreatureTypePlantBeing",
undead: "DS4.CreatureTypeUndead",
},
/**
* Define the different size categories creatures fall into
*/
creatureSizeCategories: {
tiny: "DS4.CreatureSizeCategoryTiny",
small: "DS4.CreatureSizeCategorySmall",
normal: "DS4.CreatureSizeCategoryNormal",
large: "DS4.CreatureSizeCategoryLarge",
huge: "DS4.CreatureSizeCategoryHuge",
colossal: "DS4.CreatureSizeCategoryColossal",
},
/**
* Define the base info of a creature
*/
creatureBaseInfo: {
loot: "DS4.CreatureBaseInfoLoot",
foeFactor: "DS4.CreatureBaseInfoFoeFactor",
creatureType: "DS4.CreatureBaseInfoCreatureType",
sizeCategory: "DS4.CreatureBaseInfoSizeCategory",
experiencePoints: "DS4.CreatureBaseInfoExperiencePoints",
description: "DS4.CreatureBaseInfoDescription",
},
/**
* Define translations for available distance units
*/
distanceUnits: {
meter: "DS4.UnitMeters",
kilometer: "DS4.UnitKilometers",
custom: "DS4.UnitCustom",
},
/**
* Define abbreviations for available distance units
*/
distanceUnitsAbbr: {
meter: "DS4.UnitMetersAbbr",
kilometer: "DS4.UnitKilometersAbbr",
custom: "DS4.UnitCustomAbbr",
},
/**
* Define translations for available distance units
*/
temporalUnits: {
rounds: "DS4.UnitRounds",
minutes: "DS4.UnitMinutes",
hours: "DS4.UnitHours",
days: "DS4.UnitDays",
custom: "DS4.UnitCustom",
},
/**
* Define abbreviations for available units
*/
temporalUnitsAbbr: {
rounds: "DS4.UnitRoundsAbbr",
minutes: "DS4.UnitMinutesAbbr",
hours: "DS4.UnitHoursAbbr",
days: "DS4.UnitDaysAbbr",
custom: "DS4.UnitCustomAbbr",
},
/**
* Define localization strings for Chat Visibility
*/
chatVisibilities: {
roll: "DS4.ChatVisibilityRoll",
gmroll: "DS4.ChatVisibilityGmRoll",
blindroll: "DS4.ChatVisibilityBlindRoll",
selfroll: "DS4.ChatVisibilitySelfRoll",
},
};

View file

@ -1,10 +1,12 @@
// Import Modules
import { DS4Actor } from "./actor/actor";
import { DS4ActorSheet } from "./actor/actor-sheet";
import { DS4Item } from "./item/item";
import { DS4ItemSheet } from "./item/item-sheet";
import { DS4 } from "./config";
import { DS4Check } from "./rolls/check";
import { DS4CharacterActorSheet } from "./actor/sheets/character-sheet";
import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet";
import { createCheckRoll } from "./rolls/check-factory";
Hooks.once("init", async function () {
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
@ -13,6 +15,7 @@ Hooks.once("init", async function () {
DS4Actor,
DS4Item,
DS4,
createCheckRoll,
};
// Record configuration
@ -22,6 +25,10 @@ Hooks.once("init", async function () {
CONFIG.Actor.entityClass = DS4Actor as typeof Actor;
CONFIG.Item.entityClass = DS4Item as typeof Item;
// Define localized type labels
CONFIG.Actor.typeLabels = DS4.actorTypes;
CONFIG.Item.typeLabels = DS4.itemTypes;
// Configure Dice
CONFIG.Dice.types = [Die, DS4Check];
CONFIG.Dice.terms = {
@ -32,7 +39,8 @@ Hooks.once("init", async function () {
// Register sheet application classes
Actors.unregisterSheet("core", ActorSheet);
Actors.registerSheet("ds4", DS4ActorSheet, { makeDefault: true });
Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true });
Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true });
Items.unregisterSheet("core", ItemSheet);
Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true });
@ -47,13 +55,15 @@ async function registerHandlebarsPartials() {
"systems/ds4/templates/item/partials/effects.hbs",
"systems/ds4/templates/item/partials/body.hbs",
"systems/ds4/templates/actor/partials/items-overview.hbs",
"systems/ds4/templates/actor/partials/talents-overview.hbs",
"systems/ds4/templates/actor/partials/talents-abilities-overview.hbs",
"systems/ds4/templates/actor/partials/spells-overview.hbs",
"systems/ds4/templates/actor/partials/overview-add-button.hbs",
"systems/ds4/templates/actor/partials/overview-control-buttons.hbs",
"systems/ds4/templates/actor/partials/attributes-traits.hbs",
"systems/ds4/templates/actor/partials/combat-values.hbs",
"systems/ds4/templates/actor/partials/profile.hbs",
"systems/ds4/templates/actor/partials/character-progression.hbs",
"systems/ds4/templates/actor/partials/special-creature-abilites-overview.hbs",
];
return loadTemplates(templatePaths);
}
@ -76,18 +86,28 @@ Hooks.once("setup", function () {
"armorMaterialTypes",
"armorMaterialTypesAbbr",
"armorMaterialTypes",
"spellTypes",
"spellCategories",
"attributes",
"traits",
"combatValues",
"baseInfo",
"progression",
"language",
"profile",
"currency",
"characterBaseInfo",
"characterProgression",
"characterLanguage",
"characterProfile",
"characterCurrency",
"creatureTypes",
"creatureSizeCategories",
"creatureBaseInfo",
"temporalUnits",
"temporalUnitsAbbr",
"distanceUnits",
"distanceUnitsAbbr",
"chatVisibilities",
];
// Exclude some from sorting where the default order matters
const noSort = ["attributes", "traits", "combatValues"];
const noSort = ["attributes", "traits", "combatValues", "creatureSizeCategories"];
// Localize and sort CONFIG objects
for (const o of toLocalize) {

View file

@ -1,15 +1,20 @@
import { ModifiableData } from "../actor/actor-data";
import { ModifiableData } from "../common/common-data";
import { DS4 } from "../config";
export type ItemType = keyof typeof DS4.itemTypes;
export type DS4ItemDataType =
| DS4Weapon
| DS4Armor
| DS4Shield
| DS4Spell
| DS4Trinket
| DS4Equipment
| DS4Talent
| DS4RacialAbility
| DS4Language
| DS4Alphabet;
| DS4Alphabet
| DS4SpecialCreatureAbility;
// types
@ -32,12 +37,35 @@ interface DS4TalentRank extends ModifiableData<number> {
max: number;
}
interface DS4Spell extends DS4ItemBase, DS4ItemEquipable {
spellType: "spellcasting" | "targetedSpellcasting";
bonus: string;
spellCategory:
| "healing"
| "fire"
| "ice"
| "light"
| "darkness"
| "mindAffecting"
| "electricity"
| "none"
| "unset";
maxDistance: UnitData<DistanceUnit>;
effectRadius: UnitData<DistanceUnit>;
duration: UnitData<TemporalUnit>;
cooldownDuration: UnitData<TemporalUnit>;
scrollPrice: number;
}
interface DS4Shield extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable, DS4ItemProtective {}
interface DS4Trinket extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable {}
interface DS4Equipment extends DS4ItemBase, DS4ItemPhysical {}
type DS4RacialAbility = DS4ItemBase;
type DS4Language = DS4ItemBase;
type DS4Alphabet = DS4ItemBase;
interface DS4SpecialCreatureAbility extends DS4ItemBase {
experiencePoints: number;
}
// templates
@ -62,3 +90,10 @@ interface DS4ItemEquipable {
interface DS4ItemProtective {
armorValue: number;
}
interface UnitData<UnitType> {
value: string;
unit: UnitType;
}
type TemporalUnit = "rounds" | "minutes" | "hours" | "days" | "custom";
type DistanceUnit = "meter" | "kilometer" | "custom";

View file

@ -0,0 +1,237 @@
import { DS4 } from "../config";
/**
* Provides default values for all arguments the `CheckFactory` expects.
*/
class DefaultCheckOptions implements DS4CheckFactoryOptions {
maxCritSuccess = 1;
minCritFailure = 20;
useSlayingDice = false;
rollMode: DS4RollMode = "roll";
mergeWith(other: Partial<DS4CheckFactoryOptions>): DS4CheckFactoryOptions {
return { ...this, ...other } as DS4CheckFactoryOptions;
}
}
/**
* Singleton reference for default value extraction.
*/
const defaultCheckOptions = new DefaultCheckOptions();
/**
* Most basic class responsible for generating the chat formula and passing it to the chat as roll.
*/
class CheckFactory {
constructor(
private checkTargetValue: number,
private gmModifier: number,
passedOptions: Partial<DS4CheckFactoryOptions> = {},
) {
this.checkOptions = new DefaultCheckOptions().mergeWith(passedOptions);
}
private checkOptions: DS4CheckFactoryOptions;
async execute(): Promise<ChatMessage | any> {
const rollCls: typeof Roll = CONFIG.Dice.rolls[0];
const formula = [
"ds",
this.createTargetValueTerm(),
this.createCritTerm(),
this.createSlayingDiceTerm(),
].filterJoin("");
const roll = new rollCls(formula);
const rollModeTemplate = this.checkOptions.rollMode;
console.log(rollModeTemplate);
return roll.toMessage({}, { rollMode: rollModeTemplate, create: true });
}
// Term generators
createTargetValueTerm(): string | null {
if (this.checkTargetValue !== null) {
return "v" + (this.checkTargetValue + this.gmModifier);
} else {
return null;
}
}
createCritTerm(): string | null {
const minCritRequired = this.checkOptions.minCritFailure !== defaultCheckOptions.minCritFailure;
const maxCritRequired = this.checkOptions.maxCritSuccess !== defaultCheckOptions.maxCritSuccess;
if (minCritRequired || maxCritRequired) {
return "c" + (this.checkOptions.maxCritSuccess ?? "") + "," + (this.checkOptions.minCritFailure ?? "");
} else {
return null;
}
}
createSlayingDiceTerm(): string | null {
return this.checkOptions.useSlayingDice ? "x" : null;
}
}
/**
* Asks the user for all unknown/necessary information and passes them on to perform a roll.
* @param targetValue {number} The Check Target Number ("CTN")
* @param options {Partial<DS4CheckFactoryOptions>} Options changing the behaviour of the roll and message.
*/
export async function createCheckRoll(
targetValue: number,
options: Partial<DS4CheckFactoryOptions> = {},
): Promise<ChatMessage | any> {
// Ask for additional required data;
const gmModifierData = await askGmModifier(targetValue, options);
const newOptions: Partial<DS4CheckFactoryOptions> = {
maxCritSuccess: gmModifierData.maxCritSuccess ?? options.maxCritSuccess ?? undefined,
minCritFailure: gmModifierData.minCritFailure ?? options.minCritFailure ?? undefined,
useSlayingDice: gmModifierData.useSlayingDice ?? options.useSlayingDice ?? undefined,
rollMode: gmModifierData.rollMode ?? options.rollMode ?? undefined,
};
// Create Factory
const cf = new CheckFactory(gmModifierData.checkTargetValue, gmModifierData.gmModifier, newOptions);
// Possibly additional processing
// Execute roll
await cf.execute();
}
/**
* Responsible for rendering the modal interface asking for the modifier specified by GM and (currently) additional data.
*
* @notes
* At the moment, this asks for more data than it will do after some iterations.
*
* @returns {Promise<IntermediateGmModifierData>} The data given by the user.
*/
async function askGmModifier(
targetValue: number,
options: Partial<DS4CheckFactoryOptions> = {},
{ template, title }: { template?: string; title?: string } = {},
): Promise<IntermediateGmModifierData> {
// Render model interface and return value
const usedTemplate = template ?? "systems/ds4/templates/roll/roll-options.hbs";
const usedTitle = title ?? game.i18n.localize("DS4.RollDialogDefaultTitle");
const templateData = {
cssClass: "roll-option",
title: usedTitle,
checkTargetValue: targetValue,
maxCritSuccess: options.maxCritSuccess ?? defaultCheckOptions.maxCritSuccess,
minCritFailure: options.minCritFailure ?? defaultCheckOptions.minCritFailure,
rollModes: rollModes,
config: DS4,
};
const renderedHtml = await renderTemplate(usedTemplate, templateData);
const dialogPromise = new Promise<HTMLFormElement>((resolve) => {
new Dialog(
{
title: usedTitle,
close: () => {
// Don't do anything
},
content: renderedHtml,
buttons: {
ok: {
label: game.i18n.localize("DS4.RollDialogOkButton"),
callback: (html: HTMLElement | JQuery) => {
if (!("jquery" in html)) {
throw new Error(
game.i18n.format("DS4.ErrorUnexpectedHtmlType", {
exType: "JQuery",
realType: "HTMLElement",
}),
);
} else {
const innerForm = html[0].querySelector("form");
resolve(innerForm);
}
},
},
cancel: {
label: game.i18n.localize("DS4.RollDialogCancelButton"),
callback: () => {
// Don't do anything
},
},
},
default: "ok",
},
{},
).render(true);
});
const dialogForm = await dialogPromise;
return parseDialogFormData(dialogForm, targetValue);
}
/**
* Extracts Dialog data from the returned DOM element.
* @param formData {HTMLFormElement} The filed dialog
* @param targetValue {number} The previously known target value (slated for removal once data automation is available)
*/
function parseDialogFormData(formData: HTMLFormElement, targetValue: number): IntermediateGmModifierData {
return {
checkTargetValue: parseInt(formData["ctv"]?.value) ?? targetValue,
gmModifier: parseInt(formData["gmmod"]?.value) ?? 0,
maxCritSuccess: parseInt(formData["maxcoup"]?.value) ?? defaultCheckOptions.maxCritSuccess,
minCritFailure: parseInt(formData["minfumble"]?.value) ?? defaultCheckOptions.minCritFailure,
useSlayingDice: false,
rollMode: formData["visibility"]?.value ?? defaultCheckOptions.rollMode,
};
}
/**
* Contains data that needs retrieval from an interactive Dialog.
*/
interface GmModifierData {
gmModifier: number;
rollMode: DS4RollMode;
}
/**
* Contains *CURRENTLY* necessary Data for drafting a roll.
*
* @deprecated
* Quite a lot of this information is requested due to a lack of automation:
* - maxCritSuccess
* - minCritFailure
* - useSlayingDice
* - checkTargetValue
*
* They will and should be removed once effects and data retrieval is in place.
* If a "raw" roll dialog is necessary, create another pre-porcessing Dialog
* class asking for the required information.
* This interface should then be replaced with the `GmModifierData`.
*/
interface IntermediateGmModifierData extends GmModifierData {
checkTargetValue: number;
gmModifier: number;
maxCritSuccess: number;
minCritFailure: number;
// TODO: In final version from system settings
useSlayingDice: boolean;
rollMode: DS4RollMode;
}
/**
* The minimum behavioural options that need to be passed to the factory.
*/
export interface DS4CheckFactoryOptions {
maxCritSuccess: number;
minCritFailure: number;
useSlayingDice: boolean;
rollMode: DS4RollMode;
}
/**
* Defines all possible roll modes, both for iterating and typing.
*/
const rollModes = ["roll", "gmroll", "blindroll", "selfroll"] as const;
type DS4RollModeTuple = typeof rollModes;
export type DS4RollMode = DS4RollModeTuple[number];

View file

@ -86,7 +86,7 @@ export class DS4Check extends DiceTerm {
} else {
return ds4roll(targetValueToUse, {
maxCritSuccess: this.maxCritSuccess,
minCritFail: this.minCritFailure,
minCritFailure: this.minCritFailure,
slayingDiceRepetition: slayingDiceRepetition,
useSlayingDice: slayingDiceRepetition,
});
@ -132,7 +132,6 @@ export class DS4Check extends DiceTerm {
static readonly DEFAULT_TARGET_VALUE = 10;
static readonly DEFAULT_MAX_CRIT_SUCCESS = 1;
static readonly DEFAULT_MIN_CRIT_FAILURE = 20;
// TODO: add to Type declarations
static DENOMINATION = "s";
static MODIFIERS = {
x: "explode",

View file

@ -1,13 +1,13 @@
export interface RollOptions {
maxCritSuccess: number;
minCritFail: number;
minCritFailure: number;
useSlayingDice: boolean;
slayingDiceRepetition: boolean;
}
export class DefaultRollOptions implements RollOptions {
public maxCritSuccess = 1;
public minCritFail = 20;
public minCritFailure = 20;
public useSlayingDice = false;
public slayingDiceRepetition = false;

View file

@ -48,7 +48,7 @@ export function rollCheckSingleDie(
if (rolledDie <= usedOptions.maxCritSuccess) {
return new RollResult(checkTargetValue, RollResultStatus.CRITICAL_SUCCESS, usedDice, true);
} else if (rolledDie >= usedOptions.minCritFail && !isSlayingDiceRepetition(usedOptions)) {
} else if (rolledDie >= usedOptions.minCritFailure && !isSlayingDiceRepetition(usedOptions)) {
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, usedDice, true);
} else {
if (rolledDie <= checkTargetValue) {
@ -90,7 +90,7 @@ export function rollCheckMultipleDice(
const slayingDiceRepetition = isSlayingDiceRepetition(usedOptions);
// Slaying Dice require a different handling.
if (firstResult >= usedOptions.minCritFail && !slayingDiceRepetition) {
if (firstResult >= usedOptions.minCritFailure && !slayingDiceRepetition) {
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, usedDice, true);
}

View file

@ -9,12 +9,13 @@
.side-property {
margin: 2px 0;
display: grid;
grid-template-columns: minmax(30%, auto) auto;
grid-template-columns: 40% auto;
justify-content: left;
label {
line-height: $default-input-height;
font-weight: bold;
padding-right: 3pt;
}
input,
@ -30,6 +31,17 @@
height: 100%;
margin: 0px;
}
.unit-data-pair {
display: flex;
flex-direction: row;
select {
width: 4em;
}
input {
max-width: 7em;
}
}
}
}

View file

@ -6,7 +6,7 @@
"minimumCoreVersion": "0.7.9",
"compatibleCoreVersion": "0.7.9",
"templateVersion": 2,
"author": "Johannes Loher, Gesina Schwalbe, Oliver Rümpelein",
"author": "Johannes Loher, Gesina Schwalbe, Oliver Rümpelein, Siegfried Krug",
"esmodules": ["module/ds4.js"],
"styles": ["ds4.css"],
"scripts": [],
@ -29,5 +29,6 @@
"url": "https://git.f3l.de/dungeonslayers/ds4",
"manifest": "https://git.f3l.de/dungeonslayers/ds4/-/raw/latest/src/system.json?inline=false",
"download": "https://git.f3l.de/dungeonslayers/ds4/-/jobs/artifacts/0.1.0/download?job=build",
"license": "MIT"
"license": "MIT",
"initiative": "@combatValues.initiative.total"
}

View file

@ -1,84 +1,98 @@
{
"Actor": {
"types": ["character"],
"templates": {},
"types": ["character", "creature"],
"templates": {
"base": {
"attributes": {
"body": {
"base": 0,
"mod": 0
},
"mobility": {
"base": 0,
"mod": 0
},
"mind": {
"base": 0,
"mod": 0
}
},
"traits": {
"strength": {
"base": 0,
"mod": 0
},
"constitution": {
"base": 0,
"mod": 0
},
"agility": {
"base": 0,
"mod": 0
},
"dexterity": {
"base": 0,
"mod": 0
},
"intellect": {
"base": 0,
"mod": 0
},
"aura": {
"base": 0,
"mod": 0
}
},
"combatValues": {
"hitPoints": {
"base": 0,
"mod": 0,
"value": 0
},
"defense": {
"base": 0,
"mod": 0
},
"initiative": {
"base": 0,
"mod": 0
},
"movement": {
"base": 0,
"mod": 0
},
"meleeAttack": {
"base": 0,
"mod": 0
},
"rangedAttack": {
"base": 0,
"mod": 0
},
"spellcasting": {
"base": 0,
"mod": 0
},
"targetedSpellcasting": {
"base": 0,
"mod": 0
}
}
}
},
"creature": {
"templates": ["base"],
"baseInfo": {
"loot": "",
"foeFactor": 1,
"creatureType": "humanoid",
"sizeCategory": "normal",
"experiencePoints": 0,
"description": ""
}
},
"character": {
"templates": [],
"attributes": {
"body": {
"base": 0,
"mod": 0
},
"mobility": {
"base": 0,
"mod": 0
},
"mind": {
"base": 0,
"mod": 0
}
},
"traits": {
"strength": {
"base": 0,
"mod": 0
},
"constitution": {
"base": 0,
"mod": 0
},
"agility": {
"base": 0,
"mod": 0
},
"dexterity": {
"base": 0,
"mod": 0
},
"intellect": {
"base": 0,
"mod": 0
},
"aura": {
"base": 0,
"mod": 0
}
},
"combatValues": {
"hitPoints": {
"base": 0,
"mod": 0,
"value": 0
},
"defense": {
"base": 0,
"mod": 0
},
"initiative": {
"base": 0,
"mod": 0
},
"movement": {
"base": 0,
"mod": 0
},
"meleeAttack": {
"base": 0,
"mod": 0
},
"rangedAttack": {
"base": 0,
"mod": 0
},
"spellcasting": {
"base": 0,
"mod": 0
},
"targetedSpellcasting": {
"base": 0,
"mod": 0
}
},
"templates": ["base"],
"baseInfo": {
"race": "",
"class": "",
@ -98,6 +112,7 @@
}
},
"profile": {
"biography": "",
"gender": "",
"birthday": "",
"birthplace": "",
@ -120,12 +135,14 @@
"weapon",
"armor",
"shield",
"spell",
"trinket",
"equipment",
"talent",
"racialAbility",
"language",
"alphabet"
"alphabet",
"specialCreatureAbility"
],
"templates": {
"base": {
@ -180,6 +197,33 @@
},
"alphabet": {
"templates": ["base"]
},
"spell": {
"templates": ["base", "equipable"],
"spellType": "spellcasting",
"bonus": "",
"spellCategory": "unset",
"maxDistance": {
"value": "",
"unit": "meter"
},
"effectRadius": {
"value": "",
"unit": "meter"
},
"duration": {
"value": "",
"unit": "custom"
},
"cooldownDuration": {
"value": "",
"unit": "custom"
},
"scrollPrice": 0
},
"specialCreatureAbility": {
"templates": ["base"],
"experiencePoints": 0
}
}
}

View file

@ -8,17 +8,19 @@
<div class="flexrow basic-properties">
<div class="basic-property">
<label class="basic-property-label" for="data.baseInfo.race">{{config.baseInfo.race}}</label>
<label class="basic-property-label"
for="data.baseInfo.race">{{config.characterBaseInfo.race}}</label>
<input type="text" name="data.baseInfo.race" value="{{data.baseInfo.race}}" data-dtype="String" />
</div>
<div class="basic-property">
<label class="basic-property-label" for="data.baseInfo.culture">{{config.baseInfo.culture}}</label>
<label class="basic-property-label"
for="data.baseInfo.culture">{{config.characterBaseInfo.culture}}</label>
<input type="text" name="data.baseInfo.culture" value="{{data.baseInfo.culture}}"
data-dtype="String" />
</div>
<div class="basic-property flex125">
<label class="basic-property-label"
for="data.progression.progressPoints.used">{{config.progression.progressPoints}}</label>
for="data.progression.progressPoints.used">{{config.characterProgression.progressPoints}}</label>
<div class="flexrow">
<input type="number" name="data.progression.progressPoints.used"
value="{{data.progression.progressPoints.used}}" data-dtype="Number" /><span
@ -29,7 +31,7 @@
</div>
<div class="basic-property flex125">
<label class="basic-property-label"
for="data.progression.talentPoints.used">{{config.progression.talentPoints}}</label>
for="data.progression.talentPoints.used">{{config.characterProgression.talentPoints}}</label>
<div class="flexrow">
<input type="number" name="data.progression.talentPoints.used"
value="{{data.progression.talentPoints.used}}" data-dtype="Number" /><span
@ -39,12 +41,13 @@
</div>
</div>
<div class="basic-property">
<label class="basic-property-label" for="data.baseInfo.class">{{config.baseInfo.class}}</label>
<label class="basic-property-label"
for="data.baseInfo.class">{{config.characterBaseInfo.class}}</label>
<input type="text" name="data.baseInfo.class" value="{{data.baseInfo.class}}" data-dtype="String" />
</div>
<div class="basic-property">
<label class="basic-property-label"
for="data.baseInfo.heroClass">{{config.baseInfo.heroClass}}</label>
for="data.baseInfo.heroClass">{{config.characterBaseInfo.heroClass}}</label>
<input type="text" name="data.baseInfo.heroClass" value="{{data.baseInfo.heroClass}}"
data-dtype="String" />
</div>
@ -58,26 +61,31 @@
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
<a class="item" data-tab="talents">{{localize 'DS4.HeadingTalents'}}</a>
<a class="item" data-tab="profile">{{localize "DS4.HeadingProfile"}}</a>
<a class="item" data-tab="inventory">{{localize 'DS4.HeadingInventory'}}</a>
<a class="item" data-tab="spells">{{localize 'DS4.HeadingSpells'}}</a>
<a class="item" data-tab="talents-abilities">{{localize 'DS4.HeadingTalentsAbilities'}}</a>
<a class="item" data-tab="profile">{{localize "DS4.HeadingProfile"}}</a>
<a class="item" data-tab="biography">{{localize 'DS4.HeadingBiography'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Biography Tab --}}
<div class="tab biography" data-group="primary" data-tab="description">
{{editor content=data.biography target="data.biography" button=true owner=owner editable=editable}}
</div>
{{!-- Items Tab --}}
{{> systems/ds4/templates/actor/partials/items-overview.hbs}}
{{!-- Spells Tab --}}
{{> systems/ds4/templates/actor/partials/spells-overview.hbs}}
{{!-- Talents Tab --}}
{{> systems/ds4/templates/actor/partials/talents-abilities-overview.hbs}}
{{! Profile Tab --}}
{{> systems/ds4/templates/actor/partials/profile.hbs}}
{{!-- Talents Tab --}}
{{> systems/ds4/templates/actor/partials/talents-overview.hbs}}
{{!-- Items Tab --}}
{{> systems/ds4/templates/actor/partials/items-overview.hbs}}
{{!-- Biography Tab --}}
<div class="tab biography" data-group="primary" data-tab="biography">
{{editor content=data.profile.biography target="data.profile.biography" button=true owner=owner
editable=editable}}
</div>
</section>
</form>
</form>

View file

@ -0,0 +1,78 @@
<form class="{{cssClass}} flexcol" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100" />
<div class="header-fields flexrow">
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name" /></h1>
<div class="flexrow basic-properties">
<div class="basic-property">
<label>{{config.creatureBaseInfo.creatureType}}</label>
<select name="data.baseInfo.creatureType" data-type="String">
{{#select data.baseInfo.creatureType}}
{{#each config.creatureTypes as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
<div class="basic-property">
<label class="basic-property-label"
for="data.baseInfo.loot">{{config.creatureBaseInfo.loot}}</label>
<input type="text" name="data.baseInfo.loot" value="{{data.baseInfo.loot}}" data-dtype="String" />
</div>
<div class="basic-property">
<label class="basic-property-label"
for="data.baseInfo.foeFactor">{{config.creatureBaseInfo.foeFactor}}</label>
<input type="text" name="data.baseInfo.foeFactor" value="{{data.baseInfo.foeFactor}}"
data-dtype="Number" />
</div>
<div class="basic-property">
<label>{{config.creatureBaseInfo.sizeCategory}}</label>
<select name="data.baseInfo.sizeCategory" data-type="String">
{{#select data.baseInfo.sizeCategory}}
{{#each config.creatureSizeCategories as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
<div class="basic-property">
<label class="basic-property-label"
for="data.baseInfo.experiencePoints">{{config.creatureBaseInfo.experiencePoints}}</label>
<input type="text" name="data.baseInfo.experiencePoints" value="{{data.baseInfo.experiencePoints}}"
data-dtype="Number" />
</div>
</div>
</div>
<div class="character-values">
{{> systems/ds4/templates/actor/partials/attributes-traits.hbs}}
{{> systems/ds4/templates/actor/partials/combat-values.hbs}}
</div>
</header>
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="inventory">{{localize 'DS4.HeadingInventory'}}</a>
<a class="item" data-tab="special-creature-abilites">{{localize 'DS4.HeadingSpecialCreatureAbilites'}}</a>
<a class="item" data-tab="spells">{{localize 'DS4.HeadingSpells'}}</a>
<a class="item" data-tab="description">{{localize 'DS4.HeadingDescription'}}</a>
</nav>
{{!-- Sheet Body --}}
<section class="sheet-body">
{{!-- Items Tab --}}
{{> systems/ds4/templates/actor/partials/items-overview.hbs}}
{{!-- Special Creature Abilities Tab --}}
{{> systems/ds4/templates/actor/partials/special-creature-abilites-overview.hbs}}
{{!-- Spells Tab --}}
{{> systems/ds4/templates/actor/partials/spells-overview.hbs}}
{{!-- Description Tab --}}
<div class="tab description" data-group="primary" data-tab="description">
{{editor content=data.baseInfo.description target="data.baseInfo.description" button=true owner=owner
editable=editable}}
</div>
</section>
</form>

View file

@ -1,13 +1,13 @@
<div class="progression flexrow">
<div class="progression-entry">
<h2 class="progression-label"><label for="data.progression.level">{{config.progression.level}}</label>
<h2 class="progression-label"><label for="data.progression.level">{{config.characterProgression.level}}</label>
</h2>
<input class="progression-value" type="number" name="data.progression.level" value="{{data.progression.level}}"
data-dtype="Number" />
</div>
<div class="progression-entry">
<h2 class="progression-label"><label
for="data.progression.experiencePoints">{{config.progression.experiencePoints}}</label>
for="data.progression.experiencePoints">{{config.characterProgression.experiencePoints}}</label>
</h2>
<input class="progression-value" type="number" name="data.progression.experiencePoints"
value="{{data.progression.experiencePoints}}" data-dtype="Number" />

View file

@ -1,5 +1,4 @@
{{!-- TODO: For items list: only show header, if list is not empty --}}
{{!-- TODO: Refactor to avoid code duplication with special-creature-abilites-overview and talents-overview --}}
{{!-- ======================================================================== --}}
{{!-- INLINE PARTIAL DEFINITIONS --}}
@ -15,9 +14,9 @@
--}}
{{#*inline "ifHasItemOfType"}}
{{#if (and (ne itemsArray undefined) (gt itemsArray.length 0))}}
{{> @partial-block}}
{{> @partial-block}}
{{else}}
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
{{/if}}
{{/inline}}
@ -34,24 +33,24 @@
!-- @param partial-block: hand over custom children of the flexbox in the partial block.
--}}
{{#*inline "itemListHeader" }}
<li class="item flexrow item-header">
{{!-- equipped --}}
{{#if (ne dataType 'equipment')}}
<div class="flex05" title="{{localize 'DS4.ItemEquipped'}}">E</div>
{{/if}}
{{!-- image --}}
<div class="flex05 item-image"></div>
{{!-- amount --}}
<div class="flex05 item-num-val" title="{{localize 'DS4.Quantity'}}">#</div>
{{!-- name --}}
<div class="flex3 item-name">{{localize 'DS4.ItemName'}}</div>
{{!-- item type specifics --}}
{{> @partial-block }}
{{!-- description --}}
<div class="flex4">{{localize 'DS4.HeadingDescription'}}</div>
{{!-- add button --}}
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
</li>
<li class="item flexrow item-header">
{{!-- equipped --}}
{{#if (ne dataType 'equipment')}}
<div class="flex05" title="{{localize 'DS4.ItemEquipped'}}">{{localize 'DS4.ItemEquippedAbbr'}}</div>
{{/if}}
{{!-- image --}}
<div class="flex05 item-image"></div>
{{!-- amount --}}
<div class="flex05 item-num-val" title="{{localize 'DS4.Quantity'}}">#</div>
{{!-- name --}}
<div class="flex3 item-name">{{localize 'DS4.ItemName'}}</div>
{{!-- item type specifics --}}
{{> @partial-block }}
{{!-- description --}}
<div class="flex4">{{localize 'DS4.Description'}}</div>
{{!-- add button --}}
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
</li>
{{/inline}}
{{!--
@ -59,36 +58,36 @@
!-- It is a flexbox with a child for each item value of interest.
!-- An equipped checkbox is rendered if item.data.data.equipped is defined.
!-- The partial assumes a variable item to be given in the context.
!-- If the partial is called with a partial block, the partial block
!-- If the partial is called with a partial block, the partial block
!-- content is inserted before the description.
!-- @param item: hand over the item to the partial as hash parameter
!-- @param partial-block: hand over custom children of the flexbox in the partial block.
--}}
{{#*inline "itemListEntry"}}
<li class="item flexrow" data-item-id="{{item._id}}">
{{!-- equipped --}}
{{#if (ne item.data.data.equipped undefined)}}
<input class="flex05 item-change" type="checkbox" {{checked item.data.data.equipped}} data-dtype="Boolean"
data-property="data.equipped" title="{{localize 'DS4.ItemEquipped'}}">
{{/if}}
{{!-- image --}}
<div class="flex05 item-image">
<img src="{{item.img}}" title="{{item.name}}" width="24" height="24" />
</div>
{{!-- amount --}}
<input class="flex05 item-num-val item-change" type="number" min="0" step="1" value="{{item.data.data.quantity}}" data-dtype="Number"
data-property="data.quantity" title="{{localize 'DS4.Quantity'}}">
{{!-- name --}}
<input class="flex3 item-name item-change" type="text" value="{{item.name}}" data-dtype="String"
data-property="name" title="{{localize 'DS4.ItemName'}}">
{{!-- item type specifics --}}
{{> @partial-block}}
{{!-- description --}}
<div class="flex4 item-description">{{{item.data.data.description}}}</div>
{{!-- control buttons --}}
{{> systems/ds4/templates/actor/partials/overview-control-buttons.hbs }}
</li>
<li class="item flexrow" data-item-id="{{item._id}}">
{{!-- equipped --}}
{{#if (ne item.data.data.equipped undefined)}}
<input class="flex05 item-change" type="checkbox" {{checked item.data.data.equipped}} data-dtype="Boolean"
data-property="data.equipped" title="{{localize 'DS4.ItemEquipped'}}">
{{/if}}
{{!-- image --}}
<div class="flex05 item-image">
<img src="{{item.img}}" title="{{item.name}}" width="24" height="24" />
</div>
{{!-- amount --}}
<input class="flex05 item-num-val item-change" type="number" min="0" step="1" value="{{item.data.data.quantity}}"
data-dtype="Number" data-property="data.quantity" title="{{localize 'DS4.Quantity'}}" />
{{!-- name --}}
<input class="flex3 item-name item-change" type="text" value="{{item.name}}" data-dtype="String"
data-property="name" title="{{localize 'DS4.ItemName'}}" />
{{!-- item type specifics --}}
{{> @partial-block}}
{{!-- description --}}
<div class="flex4 item-description">{{{item.data.data.description}}}</div>
{{!-- control buttons --}}
{{> systems/ds4/templates/actor/partials/overview-control-buttons.hbs }}
</li>
{{/inline}}
@ -98,15 +97,18 @@
<div class="tab inventory" data-group="primary" data-tab="inventory">
{{!-- Money--}}
<h4 class="items-list-title">{{localize 'DS4.Currency'}}</h4>
<h4 class="items-list-title">{{localize 'DS4.CharacterCurrency'}}</h4>
<ol class="items-list">
<li class="item flexrow item-header">
<label for="data.currency.gold" class="flex05">{{config.currency.gold}}</label>
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.gold" id="data.currency.gold" value="{{data.currency.gold}}" data-dtype="Number" />
<label for="data.currency.silver" class="flex05">{{config.currency.silver}}</label>
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.silver" id="data.currency.silver" value="{{data.currency.silver}}" data-dtype="Number" />
<label for="data.currency.copper" class="flex05">{{config.currency.copper}}</label>
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.copper" id="data.currency.copper" value="{{data.currency.copper}}" data-dtype="Number" />
<li class="item flexrow item-header">
<label for="data.currency.gold" class="flex05">{{config.characterCurrency.gold}}</label>
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.gold"
id="data.currency.gold" value="{{data.currency.gold}}" data-dtype="Number" />
<label for="data.currency.silver" class="flex05">{{config.characterCurrency.silver}}</label>
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.silver"
id="data.currency.silver" value="{{data.currency.silver}}" data-dtype="Number" />
<label for="data.currency.copper" class="flex05">{{config.characterCurrency.copper}}</label>
<input class="flex3 item-num-val item-change" type="number" min="0" step="1" name="data.currency.copper"
id="data.currency.copper" value="{{data.currency.copper}}" data-dtype="Number" />
</li>
</ol>
@ -116,23 +118,23 @@
{{#> ifHasItemOfType itemsArray=itemsByType.weapon dataType='weapon' }}
<ol class="items-list">
{{#> itemListHeader dataType='weapon'}}
<div class="flex05 item-image" title="{{localize 'DS4.AttackType'}}">{{localize 'DS4.AttackTypeAbbr'}}</div>
<div class="flex05 item-num-val" title="{{localize 'DS4.WeaponBonus'}}">
{{localize 'DS4.WeaponBonusAbbr'}}
</div>
<div class="flex05 item-num-val" title="{{localize 'DS4.OpponentDefense'}}">
{{localize 'DS4.OpponentDefenseAbbr'}}
</div>
<div class="flex05 item-image" title="{{localize 'DS4.AttackType'}}">{{localize 'DS4.AttackTypeAbbr'}}</div>
<div class="flex05 item-num-val" title="{{localize 'DS4.WeaponBonus'}}">
{{localize 'DS4.WeaponBonusAbbr'}}
</div>
<div class="flex05 item-num-val" title="{{localize 'DS4.OpponentDefense'}}">
{{localize 'DS4.OpponentDefenseAbbr'}}
</div>
{{/itemListHeader}}
{{#each itemsByType.weapon as |item id|}}
{{#> itemListEntry item=item}}
<div class="flex05 item-image">
<img src="{{lookup ../../config.attackTypesIcons item.data.data.attackType}}"
title="{{lookup ../../config.attackTypes item.data.data.attackType}}" width="24" height="24" />
</div>
<div class="flex05 item-num-val">{{ item.data.data.weaponBonus}}</div>
<div class="flex05 item-num-val">{{ item.data.data.opponentDefense}}</div>
{{/itemListEntry}}
{{#> itemListEntry item=item}}
<div class="flex05 item-image">
<img src="{{lookup ../../config.attackTypesIcons item.data.data.attackType}}"
title="{{lookup ../../config.attackTypes item.data.data.attackType}}" width="24" height="24" />
</div>
<div class="flex05 item-num-val">{{ item.data.data.weaponBonus}}</div>
<div class="flex05 item-num-val">{{ item.data.data.opponentDefense}}</div>
{{/itemListEntry}}
{{/each}}
</ol>
{{!-- {{else}}
@ -144,22 +146,22 @@
{{#> ifHasItemOfType itemsArray=itemsByType.armor dataType='armor' }}
<ol class="items-list">
{{#> itemListHeader dataType='armor'}}
<div title="{{localize 'DS4.ArmorMaterialType'}}">{{localize 'DS4.ArmorMaterialTypeAbbr'}}</div>
<div title="{{localize 'DS4.ArmorType'}}">{{localize 'DS4.ArmorTypeAbbr'}}</div>
<div class="flex05 item-num-val" title="{{localize 'DS4.ArmorValue'}}">
{{localize 'DS4.ArmorValueAbbr'}}
</div>
<div title="{{localize 'DS4.ArmorMaterialType'}}">{{localize 'DS4.ArmorMaterialTypeAbbr'}}</div>
<div title="{{localize 'DS4.ArmorType'}}">{{localize 'DS4.ArmorTypeAbbr'}}</div>
<div class="flex05 item-num-val" title="{{localize 'DS4.ArmorValue'}}">
{{localize 'DS4.ArmorValueAbbr'}}
</div>
{{/itemListHeader}}
{{#each itemsByType.armor as |item id|}}
{{#> itemListEntry item=item }}
<div title="{{lookup ../../config.armorMaterialTypes item.data.data.armorMaterialType}}">
{{lookup ../../config.armorMaterialTypesAbbr item.data.data.armorMaterialType}}
</div>
<div title="{{lookup ../../config.armorTypes item.data.data.armorType}}">
{{lookup ../../config.armorTypesAbbr item.data.data.armorType}}
</div>
<div class="flex05 item-num-val">{{ item.data.data.armorValue}}</div>
{{/itemListEntry}}
{{#> itemListEntry item=item }}
<div title="{{lookup ../../config.armorMaterialTypes item.data.data.armorMaterialType}}">
{{lookup ../../config.armorMaterialTypesAbbr item.data.data.armorMaterialType}}
</div>
<div title="{{lookup ../../config.armorTypes item.data.data.armorType}}">
{{lookup ../../config.armorTypesAbbr item.data.data.armorType}}
</div>
<div class="flex05 item-num-val">{{ item.data.data.armorValue}}</div>
{{/itemListEntry}}
{{/each}}
</ol>
{{/ifHasItemOfType}}
@ -170,14 +172,14 @@
{{#> ifHasItemOfType itemsArray=itemsByType.shield dataType='shield' }}
<ol class="items-list">
{{#> itemListHeader dataType='shield' }}
<div class="flex05 item-num-val" title="{{localize 'DS4.ArmorValue'}}">
{{localize 'DS4.ArmorValueAbbr'}}
</div>
<div class="flex05 item-num-val" title="{{localize 'DS4.ArmorValue'}}">
{{localize 'DS4.ArmorValueAbbr'}}
</div>
{{/itemListHeader}}
{{#each itemsByType.shield as |item id|}}
{{#> itemListEntry item=item }}
<div class="flex05 item-num-val">{{item.data.data.armorValue}}</div> {{!-- SPECIFIC --}}
{{/itemListEntry}}
{{#> itemListEntry item=item }}
<div class="flex05 item-num-val">{{item.data.data.armorValue}}</div> {{!-- SPECIFIC --}}
{{/itemListEntry}}
{{/each}}
</ol>
{{/ifHasItemOfType}}
@ -187,13 +189,13 @@
{{#> ifHasItemOfType itemsArray=itemsByType.trinket dataType='trinket' }}
<ol class="items-list">
{{#> itemListHeader dataType='trinket'}}
<div class="flex2">{{localize 'DS4.StorageLocation'}}</div>
<div class="flex2">{{localize 'DS4.StorageLocation'}}</div>
{{/itemListHeader}}
{{#each itemsByType.trinket as |item id|}}
{{#> itemListEntry item=item }}
<input class="flex2 item-change" type="text" value="{{item.data.data.storageLocation}}" data-dtype="String"
data-property="data.storageLocation" title="{{localize 'DS4.StorageLocation'}}">
{{/itemListEntry}}
{{#> itemListEntry item=item }}
<input class="flex2 item-change" type="text" value="{{item.data.data.storageLocation}}" data-dtype="String"
data-property="data.storageLocation" title="{{localize 'DS4.StorageLocation'}}">
{{/itemListEntry}}
{{/each}}
</ol>
{{/ifHasItemOfType}}
@ -203,13 +205,13 @@
{{#> ifHasItemOfType itemsArray=itemsByType.equipment dataType='equipment' }}
<ol class="items-list">
{{#> itemListHeader dataType='equipment'}}
<div class="flex2">{{localize 'DS4.StorageLocation'}}</div>
<div class="flex2">{{localize 'DS4.StorageLocation'}}</div>
{{/itemListHeader}}
{{#each itemsByType.equipment as |item id|}}
{{#> itemListEntry item=item }}
<input class="flex2 item-change" type="text" value="{{item.data.data.storageLocation}}" data-dtype="String"
data-property="data.storageLocation" title="{{localize 'DS4.StorageLocation'}}">
{{/itemListEntry}}
{{#> itemListEntry item=item }}
<input class="flex2 item-change" type="text" value="{{item.data.data.storageLocation}}" data-dtype="String"
data-property="data.storageLocation" title="{{localize 'DS4.StorageLocation'}}">
{{/itemListEntry}}
{{/each}}
</ol>
{{/ifHasItemOfType}}

View file

@ -1,13 +1,15 @@
<div class="tab profile" data-group="primary" data-tab="profile">
<div class="grid grid-2col">
{{#each data.profile as |profile-data-value profile-data-key|}}
{{#if (ne profile-data-key 'biography')}}
<div class="profile-entry">
<label for="data.profile.{{profile-data-key}}">
{{lookup ../config.profile profile-data-key}}
{{lookup ../config.characterProfile profile-data-key}}
</label>
<input type="text" name="data.profile.{{profile-data-key}}" value="{{profile-data-value}}"
data-dtype="{{lookup ../config/profileDTypes profile-data-key}}" />
data-dtype="{{lookup ../config/characterProfileDTypes profile-data-key}}" />
</div>
{{/if}}
{{/each}}
</div>
</div>
</div>

View file

@ -0,0 +1,61 @@
{{!-- TODO: Refactor to avoid code duplication with items-overview and talents-overview --}}
{{!-- ======================================================================== --}}
{{!-- INLINE PARTIAL DEFINITIONS --}}
{{!-- ======================================================================== --}}
{{!--
!-- Render a list row for a base item from a given item.
!-- Base item means it just has an image, a description, and a name (and effects).
!-- It is a flexbox with a child for each item value of interest.
!-- The partial assumes a variable item to be given in the context.
!--
!-- @param item: hand over the item to the partial as hash parameter
--}}
{{#*inline "baseItemListEntry"}}
<li class="item flexrow" data-item-id="{{item._id}}">
{{!-- image --}}
<div class="flex05 item-image">
<img src="{{item.img}}" title="{{item.name}}" width="24" height="24" />
</div>
{{!-- name --}}
<input class="flex1 item-name item-change" type="text" value="{{item.name}}" data-dtype="String"
data-property="name" title="{{localize 'DS4.ItemName'}}">
{{!-- description --}}
<div class="flex3 item-description">{{{item.data.data.description}}}</div>
{{!-- control buttons --}}
{{> systems/ds4/templates/actor/partials/overview-control-buttons.hbs }}
</li>
{{/inline}}
{{!--
!-- Render a list header for a base item list entries from a given item.
!-- The partial assumes a variable dataType to be given in the context.
!--
!-- @param dataType: the string item type for the list
--}}
{{#*inline "baseItemListHeader"}}
<li class="item flexrow item-header">
{{!-- image --}}
<div class="flex05 item-image"></div>
{{!-- name --}}
<div class="flex1 item-name">{{localize 'DS4.ItemName'}}</div>
{{!-- description --}}
<div class="flex3">{{localize 'DS4.Description'}}</div>
{{!-- add button --}}
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
</li>
{{/inline}}
{{!-- ======================================================================== --}}
<div class="tab special-creature-abilites" data-group="primary" data-tab="special-creature-abilites">
<ol class="items-list">
{{> baseItemListHeader dataType='specialCreatureAbility' }}
{{#each itemsByType.specialCreatureAbility as |item id|}}
{{> baseItemListEntry item=item}}
{{/each}}
</ol>
</div>

View file

@ -0,0 +1,93 @@
{{!-- ======================================================================== --}}
{{!-- INLINE PARTIAL DEFINITIONS --}}
{{!-- ======================================================================== --}}
{{!--
!-- Base template to display a value with unit.
!-- @param unitDatum: the object to display; must have a value and a unit attribute
!-- @param localizationString
!-- @param unitNames: mapping of allowed unitDatum.unit values to localized unit name
!-- @param unitAbbrs: mapping of allowed unitDatum.unit values to unit abbreviation
--}}
{{#*inline "unit"}}
<div class="unit-data-pair item-num-val"
title="{{localize localizationString}} [{{lookup unitNames unitDatum.unit}}]" >
{{#if unitDatum.value }}
{{unitDatum.value}}{{lookup unitAbbrs unitDatum.unit}}
{{else}}-{{/if}}
</div>
{{/inline}}
{{!--
!-- Two templates based on the "unit" template for displaying values with unit.
!-- Both accept a `config` object holding the unitNames and unitAbbr instead of
!-- directly handing over the latter two.
--}}
{{#*inline "temporalUnit"}}
{{> unit unitNames=config.temporalUnits unitAbbrs=config.temporalUnitsAbbr unitDatum=unitDatum localizationString=localizationString}}
{{/inline}}
{{#*inline "distanceUnit"}}
{{> unit unitNames=config.distanceUnits unitAbbrs=config.distanceUnitsAbbr unitDatum=unitDatum localizationString=localizationString}}
{{/inline}}
{{!-- ======================================================================== --}}
<div class="tab spells" data-group="primary" data-tab="spells">
<ol class="items-list">
<li class="item flexrow item-header">
{{!-- equipped --}}
<div class="flex05 item-image" title="{{localize 'DS4.ItemEquipped'}}">{{localize 'DS4.ItemEquippedAbbr'}}</div>
{{!-- image --}}
<div class="flex05 item-image"></div>
{{!-- name --}}
<div class="flex2 item-name">{{localize 'DS4.ItemName'}}</div>
{{!-- spell type --}}
<div class="item-image" title="{{localize 'DS4.SpellType'}}">{{localize 'DS4.SpellTypeAbbr'}}</div>
{{!-- spell bonus --}}
<div class="item-num-val" title="{{localize 'DS4.SpellBonus'}}">{{localize 'DS4.SpellBonusAbbr'}}</div>
{{!-- max. distance --}}
<div class="item-num-val" title="{{localize 'DS4.SpellMaxDistance'}}"><i class="fas fa-ruler"></i></div>
{{!-- duration --}}
<div class="item-num-val" title="{{localize 'DS4.SpellDuration'}}"><i class="far fa-clock"></i></div>
{{!-- cooldown duration --}}
<div class="item-num-val" title="{{localize 'DS4.SpellCooldownDuration'}}"><i class="fas fa-hourglass-half"></i></div>
{{!-- description --}}
{{!-- <div class="flex3">{{localize 'DS4.Description'}}</div> --}}
{{!-- add button --}}
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType='spell' }}
</li>
{{#each itemsByType.spell as |item id|}}
<li class="item flexrow" data-item-id="{{item._id}}">
<input class="flex05 item-image item-change" type="checkbox" {{checked item.data.data.equipped}} data-dtype="Boolean"
data-property="data.equipped" title="{{localize 'DS4.ItemEquipped'}}">
{{!-- image --}}
<div class="flex05 item-image">
<img src="{{item.img}}" title="{{item.name}}" width="24" height="24" />
</div>
{{!-- name --}}
<input class="flex2 item-name item-change" type="text" value="{{item.name}}" data-dtype="String"
data-property="name" title="{{localize 'DS4.ItemName'}}" />
{{!-- spell type --}}
<div class="flex05 item-image">
<img src="{{lookup ../config.spellTypesIcons item.data.data.spellType}}"
title="{{lookup ../config.spellTypes item.data.data.spellType}}" width="24" height="24" />
</div>
{{!-- spell bonus --}}
<input class="item-num-val item-change" type="text" data-dtype="String"
data-property="data.bonus" value="{{item.data.data.bonus}}" title="{{localize 'DS4.SpellBonus'}}" />
{{!-- max. distance --}}
{{> distanceUnit localizationString='DS4.SpellMaxDistance' unitDatum=item.data.data.maxDistance config=../config}}
{{!-- duration --}}
{{> temporalUnit localizationString='DS4.SpellDuration' unitDatum=item.data.data.duration config=../config}}
{{!-- cooldown duration --}}
{{> temporalUnit localizationString='DS4.SpellCooldownDuration' unitDatum=item.data.data.cooldownDuration config=../config}}
{{!-- description --}}
{{!-- <div class="flex3 item-description">{{{item.data.data.description}}}</div> --}}
{{!-- control buttons --}}
{{> systems/ds4/templates/actor/partials/overview-control-buttons.hbs }}
</li>
{{/each}}
</ol>
</div>

View file

@ -1,8 +1,8 @@
{{!-- TODO: Refactor to avoid code duplication with creature-special-abilities-overview and talents-overview --}}
{{!-- ======================================================================== --}}
{{!-- INLINE PARTIAL DEFINITIONS --}}
{{!-- ======================================================================== --}}
{{!-- TODO: remove duplicate add and delete button definition --}}
{{!--
@ -110,7 +110,7 @@
{{!-- name --}}
<div class="flex1 item-name">{{localize 'DS4.ItemName'}}</div>
{{!-- description --}}
<div class="flex3">{{localize 'DS4.HeadingDescription'}}</div>
<div class="flex3">{{localize 'DS4.Description'}}</div>
{{!-- add button --}}
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType=dataType }}
</li>
@ -120,7 +120,7 @@
{{!-- ======================================================================== --}}
<div class="tab items" data-group="primary" data-tab="talents">
<div class="tab talents-abilities" data-group="primary" data-tab="talents-abilities">
<h4 class="items-list-title">{{localize 'DS4.ItemTypeTalentPlural'}}</h4>
{{#> ifHasItemOfType itemsArray=itemsByType.talent dataType='talent' }}
<ol class="items-list">
@ -132,7 +132,7 @@
{{!-- rank info --}}
<div class="flex3">{{localize 'DS4.TalentRank'}}</div>
{{!-- description --}}
<div class="flex4">{{localize 'DS4.HeadingDescription'}}</div>
<div class="flex4">{{localize 'DS4.Description'}}</div>
{{!-- add button --}}
{{> systems/ds4/templates/actor/partials/overview-add-button.hbs dataType='talent' }}
</li>

View file

@ -3,6 +3,6 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -12,7 +12,7 @@
</select>
</div>
<div class="basic-property">
<label>{{localize "DS4.ArmorMaterialType"}}</label>
<label for="data.armorMaterialType">{{localize "DS4.ArmorMaterialType"}}</label>
<select name="data.armorMaterialType" data-type="String">
{{#select data.armorMaterialType}}
{{#each config.armorMaterialTypes as |value key|}}
@ -30,5 +30,5 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -3,5 +3,5 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -3,6 +3,6 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -13,7 +13,9 @@
<section class="sheet-body">
{{!-- Description Tab --}}
{{> systems/ds4/templates/item/partials/description.hbs}}
{{#> systems/ds4/templates/item/partials/description.hbs}}
{{> @partial-block}}
{{/systems/ds4/templates/item/partials/description.hbs}}
{{!-- Effects Tab --}}
{{> systems/ds4/templates/item/partials/effects.hbs}}

View file

@ -1,3 +1,8 @@
{{!--
Render a description tab.
Additional elements of the side-properties div can be handed over via the @partial-block.
--}}
<div class="tab flexrow" data-group="primary" data-tab="description">
<div class="side-properties">
{{#if isOwned}}
@ -24,6 +29,7 @@
{{else}}
<span>{{localize "DS4.NotOwned"}}</span>
{{/if}}
{{> @partial-block}}
</div>
<div class="description" title="{{localize 'DS4.HeadingDescription'}}">
{{editor content=data.description target="data.description" button=true owner=owner editable=editable}}

View file

@ -3,18 +3,19 @@
{{!-- As you add new fields, add them in here! --}}
<div class="side-properties">
<div class="side-property">
<label for="data.price">{{localize "DS4.PriceGold"}}</label>
<input type="number" min="0" data-dtype="Number" name="data.price" value="{{data.price}}" />
</div>
<div class="side-property">
<label for="data.availability">{{localize "DS4.ItemAvailability"}}</label>
<select name="data.availability" data-type="String">
{{#select data.availability}}
{{#each config.itemAvailabilities as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
<label for="data.price">{{localize "DS4.PriceGold"}}</label>
<input type="number" min="0" max="99999" step="0.01" data-dtype="Number"
name="data.price" value="{{data.price}}" />
</div>
<div class="side-property">
<label for="data.availability">{{localize "DS4.ItemAvailability"}}</label>
<select name="data.availability" data-type="String">
{{#select data.availability}}
{{#each config.itemAvailabilities as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
</div>

View file

@ -3,6 +3,6 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -10,5 +10,5 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -0,0 +1,15 @@
<form class="{{cssClass}}" autocomplete="off">
{{#> systems/ds4/templates/item/partials/sheet-header.hbs}}
<div class="grid grid-3col basic-properties">
<div class="basic-property">
<label>{{localize "DS4.SpecialCreatureAbilityExperiencePoints"}}</label>
<input type="number" min="0" step="1" name="data.experiencePoints" value="{{data.experiencePoints}}"
placeholder="0" data-dtype="Number" />
</div>
</div>
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -0,0 +1,72 @@
{{!-- ======================================================================== --}}
{{!-- INLINE PARTIAL DEFINITIONS --}}
{{!-- ======================================================================== --}}
{{#*inline "unitDatum" }}
<div class="side-property">
<label>{{localize localizeString}}</label>
<div class="unit-data-pair">
<input class="item-num-val" type="text" data-dtype="String"
name="data.{{property}}.value" value="{{lookup (lookup data property) 'value'}}" />
<select name="data.{{property}}.unit" data-type="String">
{{#select (lookup (lookup data property) 'unit')}}
{{#if (eq unitType 'temporal')}}
{{#each (lookup config 'temporalUnitsAbbr') as |value key|}}<option value="{{key}}">{{value}}</option>{{/each}}
{{else}}
{{#each (lookup config 'distanceUnitsAbbr') as |value key|}}<option value="{{key}}">{{value}}</option>{{/each}}
{{/if}}
{{/select}}
</select>
</div>
</div>
{{/inline}}
{{!-- ======================================================================== --}}
<form class="{{cssClass}}" autocomplete="off">
{{#> systems/ds4/templates/item/partials/sheet-header.hbs}}
<div class="grid grid-4col basic-properties">
<div class="basic-property">
<label for="data.spellType">{{localize "DS4.SpellType"}}</label>
<select name="data.spellType" data-type="String">
{{#select data.spellType}}
{{#each config.spellTypes as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
<div class="basic-property">
<label for="data.bonus">{{localize "DS4.SpellBonus"}}</label>
<input type="text" name="data.bonus" value="{{data.bonus}}" data-dtype="String" />
</div>
</div>
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{#> systems/ds4/templates/item/partials/body.hbs}}
<div class="side-property">
<label for="data.spellCategory">{{localize "DS4.SpellCategory"}}</label>
<select name="data.spellCategory" data-type="String">
{{#select data.spellCategory}}
{{#each config.spellCategories as |value key|}}
<option value="{{key}}">{{value}}</option>
{{/each}}
{{/select}}
</select>
</div>
{{> unitDatum data=data property='maxDistance' localizeString='DS4.SpellMaxDistance' unitType='distance' }}
{{> unitDatum data=data property='effectRadius' localizeString='DS4.SpellEffectRadius' unitType='distance' }}
{{> unitDatum data=data property='duration' localizeString='DS4.SpellDuration' unitType='temporal' }}
{{> unitDatum data=data property='cooldownDuration' localizeString='DS4.SpellCooldownDuration' unitType='temporal' }}
<div class="side-property">
<label for="data.scrollPrice">{{localize "DS4.SpellScrollPriceGold"}}</label>
<input type="number" min="0" max="9999" step="0.01" data-dtype="Number"
name="data.scrollPrice" value="{{data.scrollPrice}}" />
</div>
{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -27,6 +27,6 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -3,5 +3,5 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -25,5 +25,5 @@
{{/systems/ds4/templates/item/partials/sheet-header.hbs}}
{{!-- Common Item body --}}
{{> systems/ds4/templates/item/partials/body.hbs}}
{{#> systems/ds4/templates/item/partials/body.hbs}}{{/systems/ds4/templates/item/partials/body.hbs}}
</form>

View file

@ -0,0 +1,16 @@
<form class="{{cssClass}} grid">
<label for="ctv">{{localize "DS4.RollDialogTargetLabel"}}</label>
<input id="ctv" data-type="Number" type="number" name="ctv" value="{{checkTargetValue}}" />
<label for="gmmod">{{localize "DS4.RollDialogModifierLabel"}}</label>
<input id="gmmod" data-type="Number" type="number" name="gmmod" value="0" />
<label for="maxcoup">{{localize "DS4.RollDialogCoupLabel"}}</label>
<input id="maxcoup" data-type="Number" type="number" name="maxcoup" value="{{maxCritSuccess}}" />
<label for="minfumble">{{localize "DS4.RollDialogFumbleLabel"}}</label>
<input id="minfumble" data-type="Number" type="number" name="minfumble" value="{{minCritFailure}}" />
<label for="visibility">{{localize "DS4.RollDialogVisibilityLabel"}}</label>
<select id="visibility" data-type="String">
{{#each rollModes as |rollMode|}}
<option value="{{rollMode}}">{{lookup ../config.chatVisibilities rollMode}}</option>
{{/each}}
</select>
</form>