diff --git a/module.json b/module.json index d6119bb..a5ec297 100644 --- a/module.json +++ b/module.json @@ -1,33 +1,38 @@ { - "id": "ATL", - "title": "Active Token Effects", - "description": "Active Token Effects", - "authors": [ - { - "name": "Kandashi" + "id": "ATL", + "title": "Active Token Effects", + "description": "Active Token Effects", + "authors": [ + { + "name": "Kandashi" + }, + { + "name": "kaelad", + "discord": "kaelad#1693" + } + ], + "version": "v0.9.0", + "compatibility": { + "minimum": 11, + "verified": 13 }, - { - "name": "kaelad", - "discord": "kaelad#1693" - } - ], - "version": "0.6.2", - "compatibility": { - "minimum": 11, - "verified": 12 - }, - "esmodules": ["src/activeLighting.js", "src/updateManager.js"], - "packs": [ - { - "name": "premade", - "label": "Token Effects premade", - "path": "packs/premade", - "system": "dnd5e", - "type": "Item" - } - ], - "styles": ["./styles/ATL.css"], - "url": "https://github.com/kandashi/Active-Token-Lighting", - "manifest": "https://raw.githubusercontent.com/kandashi/Active-Token-Lighting/main/module.json", - "download": "https://github.com/kandashi/Active-Token-Lighting/archive/main.zip" + "esmodules": [ + "src/activeLighting.js", + "src/updateManager.js" + ], + "packs": [ + { + "name": "premade", + "label": "Token Effects premade", + "path": "packs/premade.db", + "system": "dnd5e", + "type": "Item" + } + ], + "styles": [ + "./styles/ATL.css" + ], + "url": "https://github.com/kandashi/Active-Token-Lighting", + "manifest": "https://github.com/kandashi/Active-Token-Lighting/releases/latest/download/module.json", + "download": "https://github.com/kandashi/Active-Token-Lighting/releases/download/v0.8.1/module.zip" } diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index d535cb1..0000000 --- a/package-lock.json +++ /dev/null @@ -1,842 +0,0 @@ -{ - "name": "Active-Token-Lighting", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "devDependencies": { - "@foundryvtt/foundryvtt-cli": "^1.0.3", - "fancy-log": "^2.0.0", - "yargs": "^17.7.2" - } - }, - "node_modules/@foundryvtt/foundryvtt-cli": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@foundryvtt/foundryvtt-cli/-/foundryvtt-cli-1.0.3.tgz", - "integrity": "sha512-M8NrMXFYpOEsLAbgRWfuMvUa9F6HwrROLtqhhBljqfVS1lgm0RJJY/7MObuXsTJOC6+Uu+QOPZTlw4k+hguG7w==", - "dev": true, - "dependencies": { - "chalk": "^5.2.0", - "classic-level": "^1.2.0", - "esm": "^3.2.25", - "js-yaml": "^4.1.0", - "mkdirp": "^3.0.0", - "nedb-promises": "^6.2.1", - "yargs": "^17.7.1" - }, - "bin": { - "fvtt": "fvtt.mjs" - }, - "engines": { - "node": ">17.0.0" - } - }, - "node_modules/@seald-io/binary-search-tree": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@seald-io/binary-search-tree/-/binary-search-tree-1.0.3.tgz", - "integrity": "sha512-qv3jnwoakeax2razYaMsGI/luWdliBLHTdC6jU55hQt1hcFqzauH/HsBollQ7IR4ySTtYhT+xyHoijpA16C+tA==", - "dev": true - }, - "node_modules/@seald-io/nedb": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@seald-io/nedb/-/nedb-4.0.4.tgz", - "integrity": "sha512-CUNcMio7QUHTA+sIJ/DC5JzVNNsHe743TPmC4H5Gij9zDLMbmrCT2li3eVB72/gF63BPS8pWEZrjlAMRKA8FDw==", - "dev": true, - "dependencies": { - "@seald-io/binary-search-tree": "^1.0.3", - "localforage": "^1.9.0", - "util": "^0.12.4" - } - }, - "node_modules/abstract-level": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.4.tgz", - "integrity": "sha512-eUP/6pbXBkMbXFdx4IH2fVgvB7M0JvR7/lIL33zcs0IBcwjdzSSl31TOJsaCzmKSSDF9h8QYSOJux4Nd4YJqFg==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "catering": "^2.1.0", - "is-buffer": "^2.0.5", - "level-supports": "^4.0.0", - "level-transcoder": "^1.0.1", - "module-error": "^1.0.1", - "queue-microtask": "^1.2.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/catering": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", - "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/classic-level": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.4.1.tgz", - "integrity": "sha512-qGx/KJl3bvtOHrGau2WklEZuXhS3zme+jf+fsu6Ej7W7IP/C49v7KNlWIsT1jZu0YnfzSIYDGcEWpCa1wKGWXQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "abstract-level": "^1.0.2", - "catering": "^2.1.0", - "module-error": "^1.0.1", - "napi-macros": "^2.2.2", - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/fancy-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", - "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", - "dev": true, - "dependencies": { - "color-support": "^1.1.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/level-supports": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", - "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/level-transcoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", - "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "module-error": "^1.0.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", - "dev": true, - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/localforage": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", - "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", - "dev": true, - "dependencies": { - "lie": "3.1.1" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/module-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", - "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/napi-macros": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", - "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==", - "dev": true - }, - "node_modules/nedb-promises": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/nedb-promises/-/nedb-promises-6.2.3.tgz", - "integrity": "sha512-enq0IjNyBz9Qy9W/QPCcLGh/QORGBjXbIeZeWvIjO3OMLyAvlKT3hiJubP2BKEiFniUlR3L01o18ktqgn5jxqA==", - "dev": true, - "dependencies": { - "@seald-io/nedb": "^4.0.2" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", - "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", - "dev": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 7cd086e..0000000 --- a/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "scripts": { - "build": "node ./utils/packs.mjs package pack", - "unpack": "node ./utils/packs.mjs package unpack" - }, - "devDependencies": { - "@foundryvtt/foundryvtt-cli": "^1.0.3", - "fancy-log": "^2.0.0", - "yargs": "^17.7.2" - } -} diff --git a/packs/premade.db b/packs/premade.db new file mode 100644 index 0000000..033c135 --- /dev/null +++ b/packs/premade.db @@ -0,0 +1,22 @@ +{"_id":"28vvGZ45JKhFd30k","name":"Self Vision","type":"feat","img":"icons/creatures/eyes/human-single-blue.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.dimSight","value":"0","mode":5,"priority":50},{"key":"ATL.brightSight","value":"0","mode":5,"priority":50}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/creatures/eyes/human-single-blue.webp","label":"Self Vision","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.QzF5YYBPciqhY9td"},"ATL":{"conversion":3}},"system":{"description":{"value":"

No vision

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349695,"modifiedTime":1662289349695,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"2lqBiA3sUT7ufe4U","name":"Light","type":"spell","img":"icons/magic/light/explosion-star-small-blue-yellow.webp","effects":[{"_id":"J1UQgOQwn3Di9IXC","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.light.dim","value":"40","mode":2,"priority":40},{"key":"ATL.light.bright","value":"20","mode":2,"priority":40},{"key":"ATL.light.animation","value":"{type: \"none\"}","mode":2,"priority":40},{"key":"ATL.light.color","value":"#ffffff","mode":5,"priority":40},{"key":"ATL.light.alpha","value":"0.15","mode":1,"priority":40}],"disabled":false,"duration":{"startTime":null,"seconds":3600,"rounds":600,"combat":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/light/explosion-star-small-blue-yellow.webp","label":"Light","origin":"Item.nq1GUqmEKeN8n2J7","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"betterRolls5e":{"quickOther":{"context":"","value":true,"altValue":true},"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickSave":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickVersatile":{"value":false,"altValue":false},"quickFlavor":{"value":true,"altValue":true},"quickCharges":{"value":{"use":false},"altValue":{"use":false}}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.4kvWaLh1t8qRgbyg"},"ATL":{"conversion":3}},"system":{"description":{"value":"

You touch one object that is no larger than 10 feet in any dimension. Until the spell ends, the object sheds bright light in a 20-foot radius and dim light for an additional 20 feet. The light can be colored as you like. Completely covering the object with something opaque blocks the light. The spell ends if you cast it again or dismiss it as an action.

If you target an object held or worn by a hostile creature, that creature must succeed on a Dexterity saving throw to avoid the spell.

","chat":"","unidentified":""},"source":"PHB pg. 255","activation":{"type":"action","cost":1,"condition":""},"duration":{"value":1,"units":"hour"},"target":{"value":1,"width":null,"units":"","type":"object"},"range":{"value":null,"long":null,"units":"touch"},"uses":{"value":0,"max":"0","per":""},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"save","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":"","value":""},"formula":"","save":{"ability":"dex","dc":null,"scaling":"spell"},"level":0,"school":"evo","components":{"value":"","vocal":true,"somatic":false,"material":true,"ritual":false,"concentration":false},"materials":{"value":"A firefly or phosphorescent moss.","consumed":false,"cost":0,"supply":0},"preparation":{"mode":"prepared","prepared":false},"scaling":{"mode":"none","formula":""},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349697,"modifiedTime":1662289349697,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"4Q5ejPSquOcuIlL3","name":"Bullseye Lantern","type":"consumable","img":"icons/sundries/lights/lantern-iron-yellow.webp","effects":[{"_id":"RcRIqKakmgxJ2oDN","flags":{},"changes":[{"key":"ATL.preset","mode":5,"value":"lantern","priority":null},{"key":"ATL.light.angle","mode":5,"value":"60","priority":null}],"disabled":false,"duration":{"startTime":null,"seconds":21600,"rounds":1,"combat":null,"turns":null,"startRound":null,"startTurn":null},"icon":null,"label":"Bullseye Lantern","origin":"Item.zHAFAkIzbQ23sIBG","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.5w00KV6ZgRfv5Cos"},"midi-qol":{"onUseMacroName":""},"ATL":{"conversion":3}},"system":{"description":{"value":"

A bullseye lantern casts bright light in a 60-foot cone and dim light for an additional 60 feet. Once lit, it burns for 6 hours on a flask (1 pint) of oil.

","chat":"","unidentified":""},"source":"PHB pg. 152","quantity":1,"weight":2,"price":10,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":6,"units":"hour"},"target":{"value":120,"width":null,"units":"ft","type":"cone"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"util","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349698,"modifiedTime":1662289349698,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"62EbnSziFFY6mFns","name":"Darkvision (120 ft)","type":"feat","img":"icons/magic/perception/eye-ringed-glow-angry-small-teal.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.dimSight","value":"120","mode":5,"priority":40},{"key":"ATL.brightSight","value":"0","mode":5,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/perception/eye-ringed-glow-angry-small-teal.webp","label":"Darkvision (120 ft)","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.qkyIvXCMRkgHOL2g"},"ATL":{"conversion":3}},"system":{"description":{"value":"

You touch a willing creature to grant it the ability to see in the dark. For the duration, that creature has darkvision out to a range of 120 feet.

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349699,"modifiedTime":1662289349699,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"82gVxKJhQwYBceIQ","name":"Bullseye Lantern (No preset)","type":"consumable","img":"/icons/sundries/lights/lantern-iron-yellow.webp","effects":[{"_id":"RcRIqKakmgxJ2oDN","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true,"macroRepeat":"none"},"ActiveAuras":{"isAura":false,"radius":null,"aura":"None","alignment":"","type":"","ignoreSelf":false,"height":false,"hidden":false,"hostile":false,"onlyOnce":false}},"changes":[{"key":"ATL.light.dim","value":"120","mode":2,"priority":null},{"key":"ATL.light.bright","value":"60","mode":2,"priority":null},{"key":"ATL.lockRotation","value":"false","mode":2,"priority":null},{"key":"ATL.light.angle","value":"52.5","mode":5,"priority":null},{"key":"ATL.light.animation","value":"{type: \"torch\", speed: 2, intensity: 2}","mode":5,"priority":null},{"key":"ATL.light.color","value":"#f8c377","mode":5,"priority":null},{"key":"ATL.light.alpha","value":"0.4","mode":5,"priority":null}],"disabled":false,"duration":{"startTime":null,"seconds":21600,"rounds":1,"combat":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/sundries/lights/lantern-iron-yellow.webp","label":"Bullseye Lantern (No preset)","origin":"Item.zHAFAkIzbQ23sIBG","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"betterRolls5e":{"quickOther":{"context":"","value":true,"altValue":true},"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickTemplate":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true},"quickCharges":{"value":{"quantity":false,"use":true},"altValue":{"quantity":false,"use":true}}},"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.NcJ1W7alnGm1rctm"},"midi-qol":{"onUseMacroName":""},"ATL":{"conversion":3}},"system":{"description":{"value":"

A bullseye lantern casts bright light in a 60-foot cone and dim light for an additional 60 feet. Once lit, it burns for 6 hours on a flask (1 pint) of oil.

","chat":"","unidentified":""},"source":"PHB pg. 152","quantity":1,"weight":2,"price":10,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":6,"units":"hour"},"target":{"value":120,"width":null,"units":"ft","type":"cone"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"util","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349699,"modifiedTime":1662289349699,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"C27HzBah5IVckJHb","name":"Hooded Lantern Bright (No preset)","type":"consumable","img":"icons/sundries/lights/lantern-iron-yellow.webp","effects":[{"_id":"Luhzh1SMIHJRWax6","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true,"macroRepeat":"none"},"ActiveAuras":{"isAura":false,"radius":null,"aura":"None","alignment":"","type":"","ignoreSelf":false,"height":false,"hidden":false,"hostile":false,"onlyOnce":false}},"changes":[{"key":"ATL.light.dim","mode":2,"value":"60","priority":null},{"key":"ATL.light.bright","mode":2,"value":"30","priority":null},{"key":"ATL.light.animation","mode":5,"value":"{type: \"torch\", speed: 2, intensity: 2}","priority":null},{"key":"ATL.light.color","mode":5,"value":"#f8c377","priority":null},{"key":"ATL.light.alpha","mode":5,"value":"0.4","priority":null}],"disabled":true,"duration":{"startTime":null,"seconds":21600,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/sundries/lights/lantern-iron-yellow.webp","label":"Hooded Lantern Bright (No preset)","origin":"Item.YcuVctblVhbPkCpZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"betterRolls5e":{"quickOther":{"context":"","value":true,"altValue":true},"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickTemplate":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true},"quickCharges":{"value":{"quantity":false,"use":true},"altValue":{"quantity":false,"use":true}}},"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.YamLpkUoXQ7WCfdU"},"midi-qol":{"onUseMacroName":""},"ATL":{"conversion":3}},"system":{"description":{"value":"

A hooded lantern casts bright light in a 30-foot radius and dim light for an additional 30 feet. Once lit, it burns for 6 hours on a flask (1 pint) of oil. As an action, you can lower the hood, reducing the light to dim light in a 5-foot radius.

","chat":"","unidentified":""},"source":"PHB pg. 152","quantity":1,"weight":2,"price":10,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":6,"units":"hour"},"target":{"value":60,"width":null,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"util","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349701,"modifiedTime":1662289349701,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"D5njiOGPOrpEgNiK","name":"Torch (No preset)","type":"consumable","img":"icons/sundries/lights/torch-black.webp","effects":[{"_id":"1NyUBzqqE760PG3H","flags":{"dae":{"stackable":false,"specialDuration":["None"],"transfer":true,"macroRepeat":"none"},"ActiveAuras":{"isAura":false,"radius":null,"aura":"None","alignment":"","type":"","ignoreSelf":false,"height":false,"hidden":false,"hostile":false,"onlyOnce":false}},"changes":[{"key":"ATL.light.dim","value":"40","mode":2,"priority":null},{"key":"ATL.light.bright","value":"20","mode":2,"priority":null},{"key":"ATL.light.animation","value":"{type: \"torch\", speed: 2, intensity: 2}","mode":5,"priority":null},{"key":"ATL.light.color","value":"#f8c377","mode":5,"priority":null},{"key":"ATL.light.alpha","value":"0.4","mode":5,"priority":null}],"disabled":true,"duration":{"startTime":null,"seconds":3600,"rounds":1,"combat":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/sundries/lights/torch-black.webp","label":"Torch (No preset)","origin":"Item.5FSaTgHBmJuWf43E","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Item.5FSaTgHBmJuWf43E"},"betterRolls5e":{"quickDamage":{"context":{"0":""},"value":{"0":true},"altValue":{"0":true}},"quickOther":{"context":"","value":true,"altValue":true},"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickAttack":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickTemplate":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true},"quickCharges":{"value":{"quantity":false,"use":true},"altValue":{"quantity":false,"use":true}}},"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"midi-qol":{"onUseMacroName":""},"ATL":{"conversion":3}},"system":{"description":{"value":"

A torch burns for 1 hour, providing bright light in a 20-foot radius and dim light for an additional 20 feet. If you make a melee attack with a burning torch and hit, it deals 1 fire damage.

","chat":"","unidentified":""},"source":"PHB pg. 153","quantity":1,"weight":1,"price":0.01,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":1,"units":"hour"},"target":{"value":40,"width":null,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"str","actionType":"mwak","attackBonus":"0","chatFlavor":"","critical":null,"damage":{"parts":[["1","fire"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349702,"modifiedTime":1662289349702,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"DcJ5tmXC4sM4LX1Y","name":"Moon-Touched","type":"feat","img":"icons/magic/fire/explosion-fireball-large-blue.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.light.dim","value":"30","mode":2,"priority":40},{"key":"ATL.light.bright","value":"15","mode":2,"priority":40},{"key":"ATL.light.animation","value":"{type: \"none\"}","mode":2,"priority":40},{"key":"ATL.light.color","value":"#f4f1c9","mode":2,"priority":40},{"key":"ATL.light.alpha","value":"0.15","mode":1,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/fire/explosion-fireball-large-blue.webp","label":"Moon-Touched","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.2AjMYGJCW0BUpmIs"},"ATL":{"conversion":3}},"system":{"description":{"value":"

In darkness, the unsheathed blade of this sword sheds moonlight, creating bright light in a 15-foot radius and dim light for an additional 15 feet.

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349703,"modifiedTime":1662289349703,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"Hyl30VIVU9wlUIi8","name":"None Light","type":"feat","img":"icons/magic/perception/third-eye-blue-red.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.light.dim","value":"0","mode":5,"priority":50},{"key":"ATL.light.bright","value":"0","mode":5,"priority":50},{"key":"ATL.light.animation","value":"{ \"type\":\"none\"}","mode":5,"priority":50}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/perception/third-eye-blue-red.webp","label":"None Light","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.tTOe0e59z1xuTVHx"},"ATL":{"conversion":3}},"system":{"description":{"value":"

No light

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349703,"modifiedTime":1662289349703,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"K1H7gnJ4RMzgJW0j","name":"Darkvision (60 ft)","type":"feat","img":"icons/magic/perception/eye-ringed-glow-angry-red.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.dimSight","value":"60","mode":5,"priority":40},{"key":"ATL.brightSight","value":"0","mode":5,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/perception/eye-ringed-glow-angry-red.webp","label":"Darkvision (60 ft)","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.P5C2lbFfOm8YQqrz"},"ATL":{"conversion":3}},"system":{"description":{"value":"

You touch a willing creature to grant it the ability to see in the dark. For the duration, that creature has darkvision out to a range of 60 feet.

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349705,"modifiedTime":1662289349705,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"OJNaCvl6tpqVV2xo","name":"Torch","type":"consumable","img":"icons/sundries/lights/torch-black.webp","effects":[{"_id":"1NyUBzqqE760PG3H","flags":{"dae":{"stackable":false,"specialDuration":"None","transfer":true}},"changes":[{"key":"ATL.preset","mode":5,"value":"torch","priority":null}],"disabled":true,"duration":{"startTime":null,"seconds":3600,"rounds":1,"combat":null,"turns":null,"startRound":null,"startTurn":null},"icon":null,"label":"Torchlight","origin":"Item.5FSaTgHBmJuWf43E","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Item.5FSaTgHBmJuWf43E"}},"system":{"description":{"value":"

A torch burns for 1 hour, providing bright light in a 20-foot radius and dim light for an additional 20 feet. If you make a melee attack with a burning torch and hit, it deals 1 fire damage.

","chat":"","unidentified":""},"source":"PHB pg. 153","quantity":1,"weight":1,"price":0.01,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":1,"units":"hour"},"target":{"value":40,"width":null,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"str","actionType":"mwak","attackBonus":"0","chatFlavor":"","critical":{"threshold":null,"damage":""},"damage":{"parts":[["1","fire"]],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket"},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349706,"modifiedTime":1662289349706,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"VVFbQn118VJO4Mm0","name":"Darkvision (90 ft)","type":"feat","img":"icons/magic/perception/eye-ringed-glow-angry-large-red.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.dimSight","value":"90","mode":5,"priority":40},{"key":"ATL.brightSight","value":"0","mode":5,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/perception/eye-ringed-glow-angry-large-red.webp","label":"Darkvision (90 ft)","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.WEi5oGE2Hil8enlE"},"ATL":{"conversion":3}},"system":{"description":{"value":"

You touch a willing creature to grant it the ability to see in the dark. For the duration, that creature has darkvision out to a range of 90 feet.

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349707,"modifiedTime":1662289349707,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"ViAiCc5nBxtKS6kk","name":"Darkvision (150 ft)","type":"feat","img":"icons/magic/perception/eye-ringed-glow-angry-teal.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.dimSight","value":"150","mode":5,"priority":40},{"key":"ATL.brightSight","value":"0","mode":5,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/perception/eye-ringed-glow-angry-teal.webp","label":"Darkvision (150 ft)","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.4M57GHZkmrf9cl6y"},"ATL":{"conversion":3}},"system":{"description":{"value":"

You touch a willing creature to grant it the ability to see in the dark. For the duration, that creature has darkvision out to a range of 150 feet.

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349708,"modifiedTime":1662289349708,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"Wd9eBqvPTB1kl8g0","name":"Darkvision (180 ft)","type":"feat","img":"icons/magic/perception/eye-ringed-glow-angry-large-teal.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.dimSight","value":"180","mode":5,"priority":40},{"key":"ATL.brightSight","value":"0","mode":5,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/perception/eye-ringed-glow-angry-large-teal.webp","label":"Darkvision (180 ft)","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.LG4m1UjOpb4Yatmb"},"ATL":{"conversion":3}},"system":{"description":{"value":"

You touch a willing creature to grant it the ability to see in the dark. For the duration, that creature has darkvision out to a range of 180 feet.

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349709,"modifiedTime":1662289349709,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"YsHrxBIvZmSoJT9i","name":"Darkvision (30 ft)","type":"feat","img":"icons/magic/perception/eye-ringed-glow-angry-small-red.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.dimSight","value":"30","mode":5,"priority":40},{"key":"ATL.brightSight","value":"0","mode":5,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/perception/eye-ringed-glow-angry-small-red.webp","label":"Darkvision (30 ft)","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.j5DAdjbAvQVGnDbI"},"ATL":{"conversion":3}},"system":{"description":{"value":"

You touch a willing creature to grant it the ability to see in the dark. For the duration, that creature has darkvision out to a range of 30 feet.

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349711,"modifiedTime":1662289349711,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"Zh8jEAQusnlL3LMR","name":"Candle","type":"consumable","img":"icons/sundries/lights/candle-unlit-white.webp","effects":[{"_id":"WtdgeCRYXV3Mslji","flags":{},"changes":[{"key":"ATL.preset","mode":5,"value":"candle","priority":null}],"disabled":true,"duration":{"startTime":null,"seconds":3600,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":null,"label":"Candle","origin":"Item.sh2cz4QhVQ67rRoR","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.JLtVhaS0sHOwUz9Y"}},"system":{"description":{"value":"

For 1 hour, a candle sheds bright light in a 5-foot radius and dim light for an additional 5 feet.

","chat":"","unidentified":""},"source":"PHB pg. 151","quantity":1,"weight":0,"price":0.01,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":1,"units":"hour"},"target":{"value":10,"width":null,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"util","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349712,"modifiedTime":1662289349712,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"bTsSDJvyxZ7qFRvP","name":"Goggles of Night","type":"equipment","img":"icons/magic/control/hypnosis-mesmerism-eye.webp","effects":[{"_id":"ZpV2oWMdjR0C5mAb","flags":{},"changes":[{"key":"ATL.dimSight","value":"60","mode":2,"priority":null}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/magic/control/hypnosis-mesmerism-eye.webp","label":"Goggles of Night","origin":"Item.XzSHMCkf0u1DW08n","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.wkWZVnnW5HOYpwjs"}},"system":{"description":{"value":"

Wondrous item

\n

While wearing these dark lenses, you have darkvision out to a range of 60 feet. If you already have darkvision, wearing the goggles increases its range by 60 feet.

","chat":"","unidentified":""},"source":"DMG pg 172","quantity":1,"weight":0.3,"price":1500,"attunement":0,"equipped":false,"rarity":"Uncommon","identified":true,"activation":{"type":"","cost":0,"condition":""},"duration":{"value":0,"units":""},"target":{"value":0,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":""},"consume":{"type":"","target":null,"amount":null},"ability":"","actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"armor":{"value":0,"type":"trinket","dex":0},"hp":{"value":0,"max":0,"dt":null,"conditions":""},"baseItem":"","speed":{"value":null,"conditions":""},"strength":0,"stealth":false,"proficient":false,"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349713,"modifiedTime":1662289349713,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"g41V9e7E2IuhckqF","name":"Hooded Lantern","type":"consumable","img":"icons/sundries/lights/lantern-iron-yellow.webp","effects":[{"_id":"Luhzh1SMIHJRWax6","flags":{},"changes":[{"key":"ATL.preset","mode":5,"value":"lantern","priority":null}],"disabled":true,"duration":{"startTime":null,"seconds":21600,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":null,"label":"Hooded Lantern","origin":"Item.YcuVctblVhbPkCpZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.1pP3vxbAdnaUe0ZG"}},"system":{"description":{"value":"

A hooded lantern casts bright light in a 30-foot radius and dim light for an additional 30 feet. Once lit, it burns for 6 hours on a flask (1 pint) of oil. As an action, you can lower the hood, reducing the light to dim light in a 5-foot radius.

","chat":"","unidentified":""},"source":"PHB pg. 152","quantity":1,"weight":2,"price":10,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":6,"units":"hour"},"target":{"value":60,"width":null,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"util","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349714,"modifiedTime":1662289349714,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"gWyWIB5mFSsB9u0M","name":"Lamp (No Preset)","type":"consumable","img":"icons/commodities/treasure/lantern-stone-grey.webp","effects":[{"_id":"HmVtWWjCq26FlwOR","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true,"macroRepeat":"none"},"ActiveAuras":{"isAura":false,"radius":null,"aura":"None","alignment":"","type":"","ignoreSelf":false,"height":false,"hidden":false,"hostile":false,"onlyOnce":false}},"changes":[{"key":"ATL.light.dim","value":"45","mode":2,"priority":40},{"key":"ATL.light.bright","value":"15","mode":2,"priority":40},{"key":"ATL.light.animation","value":"{type: \"torch\", speed: 2, intensity: 2}","mode":2,"priority":40},{"key":"ATL.light.color","value":"#f8c377","mode":5,"priority":40},{"key":"ATL.light.alpha","value":"0.4","mode":5,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":21600,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/commodities/treasure/lantern-stone-grey.webp","label":"Lamp (No preset)","origin":"Item.Yit7gV08sqkp2Pkq","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"betterRolls5e":{"quickOther":{"context":"","value":true,"altValue":true},"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickTemplate":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true},"quickCharges":{"value":{"quantity":false,"use":true},"altValue":{"quantity":false,"use":true}}},"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.Lzu2BUqfQzerRSIZ"},"midi-qol":{"onUseMacroName":""},"ATL":{"conversion":3}},"system":{"description":{"value":"

A lamp casts bright light in a 15-foot radius and dim light for an additional 30 feet. Once lit, it burns for 6 hours on a flask (1 pint) of oil

","chat":"","unidentified":""},"source":"PHB pg. 152","quantity":1,"weight":0,"price":0.5,"attunement":0,"equipped":false,"rarity":"Common","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":6,"units":"hour"},"target":{"value":45,"width":null,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"util","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349715,"modifiedTime":1662289349715,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"rWZFc6F3FewUjcp0","name":"Devil's Sight (Warlock)","type":"feat","img":"icons/creatures/eyes/lizard-single-slit-blue.webp","effects":[{"_id":"pd6hlVPQZRpF1J40","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true}},"changes":[{"key":"ATL.dimSight","value":"0","mode":5,"priority":40},{"key":"ATL.brightSight","value":"120","mode":5,"priority":40}],"disabled":true,"duration":{"startTime":null,"seconds":null,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/creatures/eyes/lizard-single-slit-blue.webp","label":"Devil's Sight (Warlock)","origin":"Item.wV8s4Ac0vXNTK2bZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"betterRolls5e":{"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickOther":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true}},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.cyv4m539eAHrV54Z"},"ATL":{"conversion":3}},"system":{"description":{"value":"

You can see normally in darkness, both magical and nonmagical, to a distance of 120 feet.

","chat":"","unidentified":""},"source":"","activation":{"type":"","cost":0,"condition":""},"duration":{"value":null,"units":""},"target":{"value":null,"width":null,"units":"","type":""},"range":{"value":null,"long":null,"units":""},"uses":{"value":0,"max":0,"per":null},"consume":{"type":"","target":null,"amount":null},"ability":null,"actionType":"","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"requirements":"","recharge":{"value":null,"charged":false},"attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349716,"modifiedTime":1662289349716,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"wRbyEp7u3etchULN","name":"Hooded Lantern Dim (No preset)","type":"consumable","img":"icons/sundries/lights/lantern-iron-yellow.webp","effects":[{"_id":"Luhzh1SMIHJRWax6","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true,"macroRepeat":"none"},"ActiveAuras":{"isAura":false,"radius":null,"aura":"None","alignment":"","type":"","ignoreSelf":false,"height":false,"hidden":false,"hostile":false,"onlyOnce":false}},"changes":[{"key":"ATL.light.dim","value":"5","mode":2,"priority":null},{"key":"ATL.light.bright","value":"0","mode":2,"priority":null},{"key":"ATL.light.animation","value":"{type: \"torch\", speed: 2, intensity: 2}","mode":5,"priority":null},{"key":"ATL.light.color","value":"#f8c377","mode":5,"priority":null},{"key":"ATL.light.alpha","value":"0.4","mode":5,"priority":null}],"disabled":true,"duration":{"startTime":null,"seconds":21600,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/sundries/lights/lantern-iron-yellow.webp","label":"Hooded Lantern Dim (No preset)","origin":"Item.YcuVctblVhbPkCpZ","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"betterRolls5e":{"quickOther":{"context":"","value":true,"altValue":true},"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickTemplate":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true},"quickCharges":{"value":{"quantity":false,"use":true},"altValue":{"quantity":false,"use":true}}},"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.SGkTqwYFV7HYADcj"},"midi-qol":{"onUseMacroName":""},"ATL":{"conversion":3}},"system":{"description":{"value":"

A hooded lantern casts bright light in a 30-foot radius and dim light for an additional 30 feet. Once lit, it burns for 6 hours on a flask (1 pint) of oil. As an action, you can lower the hood, reducing the light to dim light in a 5-foot radius.

","chat":"","unidentified":""},"source":"PHB pg. 152","quantity":1,"weight":2,"price":10,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":6,"units":"hour"},"target":{"value":60,"width":null,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"util","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349716,"modifiedTime":1662289349716,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} +{"_id":"xvOw25hdXNTIwbOn","name":"Candle (No preset)","type":"consumable","img":"icons/sundries/lights/candle-unlit-white.webp","effects":[{"_id":"WtdgeCRYXV3Mslji","flags":{"dae":{"stackable":false,"specialDuration":[],"transfer":true,"macroRepeat":"none"},"ActiveAuras":{"isAura":false,"radius":null,"aura":"None","alignment":"","type":"","ignoreSelf":false,"height":false,"hidden":false,"hostile":false,"onlyOnce":false}},"changes":[{"key":"ATL.light.dim","value":"10","mode":2,"priority":null},{"key":"ATL.light.bright","value":"5","mode":2,"priority":null},{"key":"ATL.light.color","value":"#f8c377","mode":5,"priority":null},{"key":"ATL.light.alpha","value":"0.4","mode":5,"priority":null}],"disabled":true,"duration":{"startTime":null,"seconds":3600,"combat":null,"rounds":null,"turns":null,"startRound":null,"startTurn":null},"icon":"icons/sundries/lights/candle-unlit-white.webp","label":"Candle (No preset)","origin":"Item.sh2cz4QhVQ67rRoR","tint":null,"transfer":true}],"folder":null,"sort":0,"flags":{"betterRolls5e":{"quickOther":{"context":"","value":true,"altValue":true},"critRange":{"value":null},"critDamage":{"value":""},"quickDesc":{"value":true,"altValue":true},"quickProperties":{"value":true,"altValue":true},"quickTemplate":{"value":true,"altValue":true},"quickFlavor":{"value":true,"altValue":true},"quickCharges":{"value":{"quantity":false,"use":true},"altValue":{"quantity":false,"use":true}}},"magicitems":{"enabled":false,"equipped":false,"attuned":false,"charges":"0","chargeType":"c1","destroy":false,"destroyFlavorText":"reaches 0 charges: it crumbles into ashes and is destroyed.","rechargeable":false,"recharge":"0","rechargeType":"t1","rechargeUnit":"r1","sorting":"l"},"core":{"sourceId":"Compendium.ATL.Token Lighting Premade.2uPOThTloRTLlUqY"},"midi-qol":{"onUseMacroName":""},"ATL":{"conversion":3}},"system":{"description":{"value":"

For 1 hour, a candle sheds bright light in a 5-foot radius and dim light for an additional 5 feet.

","chat":"","unidentified":""},"source":"PHB pg. 151","quantity":1,"weight":0,"price":0.01,"attunement":0,"equipped":false,"rarity":"","identified":true,"activation":{"type":"action","cost":1,"condition":""},"duration":{"value":1,"units":"hour"},"target":{"value":10,"width":null,"units":"ft","type":"radius"},"range":{"value":null,"long":null,"units":""},"uses":{"value":1,"max":"1","per":"charges","autoDestroy":false,"autoUse":true},"consume":{"type":"","target":"","amount":null},"ability":"","actionType":"util","attackBonus":0,"chatFlavor":"","critical":null,"damage":{"parts":[],"versatile":""},"formula":"","save":{"ability":"","dc":null,"scaling":"spell"},"consumableType":"trinket","attributes":{"spelldc":10}},"ownership":{"default":0,"E4BVikjIkVl2lL2j":3},"_stats":{"systemId":"dnd5e","systemVersion":"2.0.2","coreVersion":"10.284","createdTime":1662289349718,"modifiedTime":1662289349718,"lastModifiedBy":"AGKnpiEshwvZDOrK"}} diff --git a/packs/premade/000005.ldb b/packs/premade/000005.ldb new file mode 100644 index 0000000..ad8a988 Binary files /dev/null and b/packs/premade/000005.ldb differ diff --git a/packs/premade/000184.log b/packs/premade/000184.log new file mode 100644 index 0000000..e69de29 diff --git a/packs/premade/CURRENT b/packs/premade/CURRENT new file mode 100644 index 0000000..d3170b5 --- /dev/null +++ b/packs/premade/CURRENT @@ -0,0 +1 @@ +MANIFEST-000182 diff --git a/packs/premade/LOCK b/packs/premade/LOCK new file mode 100644 index 0000000..e69de29 diff --git a/packs/premade/LOG b/packs/premade/LOG new file mode 100644 index 0000000..541100a --- /dev/null +++ b/packs/premade/LOG @@ -0,0 +1,8 @@ +2025/09/17-19:00:26.184282 7aaca53fc6c0 Recovering log #180 +2025/09/17-19:00:26.186718 7aaca53fc6c0 Delete type=0 #180 +2025/09/17-19:00:26.186746 7aaca53fc6c0 Delete type=3 #178 +2025/09/18-15:52:30.908056 7aa9f09ff6c0 Level-0 table #185: started +2025/09/18-15:52:30.908259 7aa9f09ff6c0 Level-0 table #185: 0 bytes OK +2025/09/18-15:52:30.909457 7aa9f09ff6c0 Delete type=0 #183 +2025/09/18-15:52:30.913622 7aa9f09ff6c0 Manual compaction at level-0 from '!items!28vvGZ45JKhFd30k' @ 72057594037927935 : 1 .. '!items.effects!xvOw25hdXNTIwbOn.WtdgeCRYXV3Mslji' @ 0 : 0; will stop at (end) +2025/09/18-15:52:30.913714 7aa9f09ff6c0 Manual compaction at level-1 from '!items!28vvGZ45JKhFd30k' @ 72057594037927935 : 1 .. '!items.effects!xvOw25hdXNTIwbOn.WtdgeCRYXV3Mslji' @ 0 : 0; will stop at (end) diff --git a/packs/premade/LOG.old b/packs/premade/LOG.old new file mode 100644 index 0000000..3b75fa0 --- /dev/null +++ b/packs/premade/LOG.old @@ -0,0 +1,8 @@ +2025/09/17-18:45:23.762502 7aaca6bff6c0 Recovering log #176 +2025/09/17-18:45:23.764775 7aaca6bff6c0 Delete type=0 #176 +2025/09/17-18:45:23.764800 7aaca6bff6c0 Delete type=3 #174 +2025/09/17-19:00:16.432767 7aa9f09ff6c0 Level-0 table #181: started +2025/09/17-19:00:16.432796 7aa9f09ff6c0 Level-0 table #181: 0 bytes OK +2025/09/17-19:00:16.433953 7aa9f09ff6c0 Delete type=0 #179 +2025/09/17-19:00:16.435127 7aa9f09ff6c0 Manual compaction at level-0 from '!items!28vvGZ45JKhFd30k' @ 72057594037927935 : 1 .. '!items.effects!xvOw25hdXNTIwbOn.WtdgeCRYXV3Mslji' @ 0 : 0; will stop at (end) +2025/09/17-19:00:16.435195 7aa9f09ff6c0 Manual compaction at level-1 from '!items!28vvGZ45JKhFd30k' @ 72057594037927935 : 1 .. '!items.effects!xvOw25hdXNTIwbOn.WtdgeCRYXV3Mslji' @ 0 : 0; will stop at (end) diff --git a/packs/premade/MANIFEST-000182 b/packs/premade/MANIFEST-000182 new file mode 100644 index 0000000..1bcabad Binary files /dev/null and b/packs/premade/MANIFEST-000182 differ diff --git a/src/activeLighting.js b/src/activeLighting.js index 53ad601..f3dc59f 100644 --- a/src/activeLighting.js +++ b/src/activeLighting.js @@ -7,184 +7,90 @@ class ATL { static init() { let defaultPresets = [ - { - name: "torch", - light: { - dim: 40, - bright: 20, - color: "#a2642a", - animation: { - 'type': 'torch', - 'speed': 1, - 'intensity': 1 - }, - alpha: 0.7, - }, - id: "ATLPresetTorch" - }, - { - name: "lantern", - light: { - dim: 60, - bright: 30, - color: "#a2642a", - animation: { - 'type': 'torch', - 'speed': 1, - 'intensity': 1 - }, - alpha: 0.4 - }, - id: "ATLPresetLantern" - - }, - { - name: "candle", - light: { - dim: 10, - bright: 2, - color: "#a2642a", - animation: { - 'type': 'torch', - 'speed': 1, - 'intensity': 1 - }, - alpha: 0.2 - }, - id: "ATLPresetCandle" - - }, - { - name: "flashlight", - light: { - dim: 60, - bright: 30, - color: "#8bfdf6", - alpha: 0.3 - }, - id: "ATLPresetFlashlight" - } - ] + { name: "torch", light: { dim: 40, bright: 20, color: "#a2642a", animation: { type: "torch", speed: 1, intensity: 1 }, alpha: 0.7 }, id: "ATLPresetTorch" }, + { name: "lantern", light: { dim: 60, bright: 30, color: "#a2642a", animation: { type: "torch", speed: 1, intensity: 1 }, alpha: 0.4 }, id: "ATLPresetLantern" }, + { name: "candle", light: { dim: 10, bright: 2, color: "#a2642a", animation: { type: "torch", speed: 1, intensity: 1 }, alpha: 0.2 }, id: "ATLPresetCandle" }, + { name: "flashlight", light: { dim: 60, bright: 30, color: "#8bfdf6", alpha: 0.3 }, id: "ATLPresetFlashlight" } + ]; game.settings.register("ATL", "size", { name: "Size Adjustment with Flags", hint: "Allow for size adjustment to be made with flags, always returns tokens to prototype token defaults if flag is not present", - scope: "world", - config: true, - default: false, - type: Boolean, - }); - game.settings.register("ATL", "presets", { - scope: "world", - config: false, - default: defaultPresets, - type: Object, - }); - game.settings.register("ATL", "conversion", { - name: "conversion level", - scope: "world", - config: false, - default: "0.2.15", - type: String, + scope: "world", config: true, default: false, type: Boolean }); - - + game.settings.register("ATL", "presets", { scope: "world", config: false, default: defaultPresets, type: Object }); + game.settings.register("ATL", "conversion", { name: "conversion level", scope: "world", config: false, default: "0.2.15", type: String }); } static async ready() { const newTransferral = game.release.generation >= 11 && !CONFIG.ActiveEffect.legacyTransferral; const getEffects = (actor) => { if (!actor) return []; - // get the "active" effects on the actor let effects; if (newTransferral) effects = actor.appliedEffects; - else if (game.system.id === "wfrp4e") - effects = actor.actorEffects.filter((e) => !e.disabled && !e.isSuppressed); + else if (game.system.id === "wfrp4e") effects = actor.actorEffects.filter((e) => !e.disabled && !e.isSuppressed); else effects = actor.effects.filter((e) => !e.disabled && !e.isSuppressed); - // only return effects that have some ATL changes in them return effects.filter(e => e.changes.some(c => c.key.startsWith("ATL."))); }; Hooks.on("updateActiveEffect", async (effect, change, options, userId) => { - // same user if (game.userId !== userId) return; - // check that the effect is on an actor or an embedded item (for new transferral) let actor; if (effect.parent instanceof Actor) actor = effect.parent; - else if (newTransferral && effect.parent?.parent instanceof Actor) - actor = effect.parent.parent; + else if (newTransferral && effect.parent?.parent instanceof Actor) actor = effect.parent.parent; else return; - // apply the effects - let ATLeffects = getEffects(actor); - ATL.applyEffects(actor, ATLeffects); - }) + ATL.applyEffects(actor, getEffects(actor)); + }); Hooks.on("createActiveEffect", async (effect, options, userId) => { - // same user and effect is active if (game.userId !== userId || effect.disabled || effect.isSuppressed) return; - // check that the effect is on an actor or an embedded item (for new transferral) let actor; if (effect.parent instanceof Actor) actor = effect.parent; - else if (newTransferral && effect.parent?.parent instanceof Actor) - actor = effect.parent.parent; + else if (newTransferral && effect.parent?.parent instanceof Actor) actor = effect.parent.parent; else return; - // there's at least one ATL-related effect if (!effect.changes?.some(c => c.key.startsWith("ATL."))) return; - // apply the effects - let ATLeffects = getEffects(actor); - ATL.applyEffects(actor, ATLeffects); - }) + ATL.applyEffects(actor, getEffects(actor)); + }); Hooks.on("deleteActiveEffect", async (effect, options, userId) => { - // same user and effect is active if (game.userId !== userId || effect.disabled || effect.isSuppressed) return; - // check that the effect is on an actor or an embedded item (for new transferral) let actor; if (effect.parent instanceof Actor) actor = effect.parent; - else if (newTransferral && effect.parent?.parent instanceof Actor) - actor = effect.parent.parent; + else if (newTransferral && effect.parent?.parent instanceof Actor) actor = effect.parent.parent; else return; - // there's at least one ATL-related effect if (!effect.changes?.some(c => c.key.startsWith("ATL."))) return; - // apply the effects - let ATLeffects = getEffects(actor); - ATL.applyEffects(actor, ATLeffects); - }) + ATL.applyEffects(actor, getEffects(actor)); + }); Hooks.on("createToken", (doc, options, userId) => { if (game.userId !== userId) return; - let ATLeffects = getEffects(doc.actor) - if (ATLeffects.length > 0) ATL.applyEffects(doc.actor, ATLeffects) - }) + const eff = getEffects(doc.actor); + if (eff.length) ATL.applyEffects(doc.actor, eff); + }); Hooks.on("canvasReady", () => { const firstGM = game.users?.find(u => u.isGM && u.active); if (game.userId !== firstGM?.id) return; - let linkedTokens = canvas.tokens.placeables.filter(t => !t.document.link) - for (let token of linkedTokens) { - let ATLeffects = getEffects(token.actor) - if (ATLeffects.length > 0) ATL.applyEffects(token.actor, ATLeffects) + const linkedTokens = canvas.tokens.placeables.filter(t => !t.document.link); + for (const token of linkedTokens) { + const eff = getEffects(token.actor); + if (eff.length) ATL.applyEffects(token.actor, eff); } - }) + }); Hooks.on("updateItem", (item, change, options, userId) => { if (game.userId !== userId || !item.parent) return; if ((game.system.id === "dnd5e" && (hasProperty(change, "system.equipped") || hasProperty(change, "system.attunement"))) || (game.system.id === "wfrp4e" && hasProperty(change, "system.worn.value")) || (game.system.id === "swade" && hasProperty(change, "system.equipStatus"))) { - let actor = item.parent - let ATLeffects = getEffects(actor) - ATL.applyEffects(actor, ATLeffects) + const actor = item.parent; + ATL.applyEffects(actor, getEffects(actor)); } - }) + }); - // only register these hooks for v11's new transferral mode if (newTransferral) { const createDeleteItem = (item, options, userId) => { - // same user and it's an item on an actor if (game.userId !== userId || !(item.parent instanceof Actor)) return; - // there's at least one ATL-related effect if (!item.effects.some(e => e.changes.some(c => c.key.startsWith("ATL.")))) return; - // apply the effects const actor = item.parent; ATL.applyEffects(actor, actor.appliedEffects); }; @@ -194,105 +100,170 @@ class ATL { const firstGM = game.users?.find(u => u.isGM && u.active); if (game.userId !== firstGM?.id) return; - let linkedTokens = canvas.tokens.placeables.filter(t => !t.document.link) - for (let token of linkedTokens) { - let ATLeffects = getEffects(token.actor) - if (ATLeffects.length > 0) ATL.applyEffects(token.actor, ATLeffects) + const linkedTokens = canvas.tokens.placeables.filter(t => !t.document.link); + for (const token of linkedTokens) { + const eff = getEffects(token.actor); + if (eff.length) ATL.applyEffects(token.actor, eff); } } - static async UpdatePresets() { - let presets = await game.settings.get("ATL", "presets") - let content = `
` - let presetSelector = new Dialog({ + // ---------- Application V2 Preset Picker ---------- + static ATLPresetSelector = class ATLPresetSelector extends foundry.applications.api.ApplicationV2 { + static DEFAULT_OPTIONS = { + id: "atl-preset-selector", title: "Preset Selector", - content: ` -
- - ${content} -
`, - content, - buttons: { - one: { - label: "Update", - icon: ``, - callback: (html) => { - let updatePreset = html.find("[name=presets]")[0].value; - let preset = presets.find(p => p.id === updatePreset) - new PresetConfig(preset).render(true); - } - }, - two: { - label: "Create Copy", - icon: ``, - callback: (html) => { - let updatePreset = html.find("[name=presets]")[0].value; - let preset = presets.find(p => p.id === updatePreset) - // copy and remove ID so it's created as new - preset = deepClone(preset); - delete preset.id; - new PresetConfig(preset).render(true); - } - }, - three: { - label: "Delete", - icon: ``, - callback: (html) => { - let updatePreset = html.find("[name=presets]")[0].value; - let index = presets.findIndex(p => p.id === updatePreset); - new Dialog({ - title: "Conformation", - content: `Are you sure you want to remove this preset`, - buttons: { - one: { - label: "Confirm", - icon: ``, - callback: () => { - presets.splice(index, 1); - game.settings.set("ATL", "presets", presets); - } - }, - two: { - label: "Return", - icon: ``, - callback: () => presetSelector.render(true) - } - } - }).render(true) - } - }, - four: { - label: "Add New", - icon: ``, - callback: () => new PresetConfig().render(true) - } + classes: ["atl", "preset-selector"], + width: 420, + height: "auto", + resizable: true, + modal: true + }; + + async _prepareContext(_options) { + // Clean up any incomplete presets so labels don't show "undefined" + const presets = await game.settings.get("ATL", "presets"); + const safe = (presets ?? []).map(p => ({ id: p.id, name: p.name ?? "(unnamed preset)" })); + return { presets: safe }; + } + + async _renderHTML(context, _options) { + const opts = (context.presets ?? []) + .map(p => ``).join(""); + return ` +
+
+ + +
+ +
`; + } + + async _replaceHTML(elementOrHtml, htmlOrElement, _options) { + // Normalize parameters in case Foundry passes them swapped. + let target = elementOrHtml; + let content = htmlOrElement; + + // If the first arg is a string (our HTML) and 2nd is an element, swap them. + const isHTMLElement = (n) => n && typeof n === "object" && n.nodeType === 1; + if (typeof target === "string" && isHTMLElement(content)) { + [target, content] = [content, target]; } - }); - presetSelector.render(true) + + // Resolve target element + if (!isHTMLElement(target)) { + // Fall back to the application root element if needed + target = this.element ?? null; + } + if (!target) return; + + // Inject content + if (typeof content === "string") { + target.innerHTML = content; + } else if (isHTMLElement(content)) { + target.replaceChildren(content); + } else { + target.innerHTML = String(content ?? ""); + } + + // Bind listeners using a fresh context (mirrors render flow) + const context = await this._prepareContext(_options); + const root = target; + + const getSelectedPreset = () => { + const id = root.querySelector('select[name="presets"]')?.value; + const presets = context.presets ?? []; + return presets.find(p => p.id === id); + }; + + root.querySelector('[data-action="update"]')?.addEventListener("click", async () => { + const sel = getSelectedPreset(); + if (!sel) return ui.notifications.warn("ATL: No preset selected."); + // Load full preset object from settings to pass to PresetConfig + const all = await game.settings.get("ATL", "presets"); + const preset = (all ?? []).find(p => p.id === sel.id); + if (!preset) return ui.notifications.warn("ATL: Preset not found."); + new PresetConfig(preset).render(true); + }); + + root.querySelector('[data-action="copy"]')?.addEventListener("click", async () => { + const sel = getSelectedPreset(); + if (!sel) return ui.notifications.warn("ATL: No preset selected."); + const all = await game.settings.get("ATL", "presets"); + const preset = (all ?? []).find(p => p.id === sel.id); + if (!preset) return ui.notifications.warn("ATL: Preset not found."); + const copy = deepClone(preset); + delete copy.id; + new PresetConfig(copy).render(true); + }); + + root.querySelector('[data-action="delete"]')?.addEventListener("click", async () => { + const sel = getSelectedPreset(); + if (!sel) return ui.notifications.warn("ATL: No preset selected."); + const confirmed = await Dialog.confirm({ + title: "Confirmation", + content: `

Are you sure you want to remove this preset?

` + }); + if (!confirmed) return; + const presets = await game.settings.get("ATL", "presets"); + const idx = (presets ?? []).findIndex(p => p.id === sel.id); + if (idx >= 0) { + presets.splice(idx, 1); + await game.settings.set("ATL", "presets", presets); + this.render(true); + } + }); + + root.querySelector('[data-action="add"]')?.addEventListener("click", () => { + new PresetConfig().render(true); + }); + } + }; + + static async UpdatePresets() { + new ATL.ATLPresetSelector().render(true); } - static getSceneControlButtons(buttons) { - let tokenButton = buttons.find(b => b.name == "lighting") - if (tokenButton) { - tokenButton.tools.push({ - name: "atl-lights", + // ---- Scene Controls (PR-style v13) ---- + static getSceneControlButtons(controls) { + if (game.release.generation >= 13) { + if (!game.user.isGM) return; + controls.lighting.tools.atlLights = { + name: "atlLights", title: "ATL Presets", icon: "fas fa-plus-circle", - visible: game.user.isGM, - onClick: () => ATL.UpdatePresets(), - button: true - }); + button: true, + toggle: false, + onClick: () => ATL.UpdatePresets() + }; + } else { + const tokenButton = controls.find(b => b.name === "lighting"); + if (tokenButton) { + tokenButton.tools.push({ + name: "atl-lights", + title: "ATL Presets", + icon: "fas fa-plus-circle", + visible: game.user.isGM, + onClick: () => ATL.UpdatePresets(), + button: true + }); + } } } + static async applyEffects(entity, effects) { if (entity.documentName !== "Actor") return; const tokenArray = entity.getActiveTokens(); if (!tokenArray.length) return; - // Organize non-disabled effects by their application priority - const changes = effects.reduce((changes, e) => { - if (e.disabled || e.isSuppressed) return changes; - return changes.concat(e.changes.map(c => { + const changes = effects.reduce((acc, e) => { + if (e.disabled || e.isSuppressed) return acc; + return acc.concat(e.changes.map(c => { c = duplicate(c); c.effect = e; c.priority = c.priority ?? (c.mode * 10); @@ -305,236 +276,163 @@ class ATL { let originalDelta = token.document.flags.ATL?.originals || {}; const originals = mergeObject(token.document.toObject(), originalDelta); let overrides = {}; - - // helper function to apply to overrides and originalDelta const applyOverride = (key, value, preValue) => { setProperty(overrides, key, value); if (!hasProperty(originalDelta, key)) setProperty(originalDelta, key, preValue); }; - // Apply all changes - for (let change of changes) { + for (const change of changes) { if (!change.key.includes("ATL")) continue; - let updateKey = change.key.slice(4) + const updateKey = change.key.slice(4); if (updateKey === "preset") { - // get the matching preset - let presetArray = game.settings.get("ATL", "presets") - let preset = presetArray.find(i => i.name === change.value) - if (!preset) { - console.error(`ATL: No preset ${change.value} found`) - continue; - } + const presetArray = game.settings.get("ATL", "presets"); + let preset = presetArray.find(i => i.name === change.value); + if (!preset) { console.error(`ATL: No preset ${change.value} found`); continue; } preset = flattenObject(preset); - // validate preset data - for (const [key, value] of Object.entries(preset)) { - if (value === "" || value === undefined || value === null) delete preset[key]; - } - const checkString = (element) => typeof element === "string" - if ([preset["light.dim"], preset["light.bright"], preset["sight.range"]].some(checkString)) { - ui.notifications.error("ATL: preset string error") - } + for (const [k, v] of Object.entries(preset)) if (v === "" || v === undefined || v === null) delete preset[k]; + const checkString = (el) => typeof el === "string"; + if ([preset["light.dim"], preset["light.bright"], preset["sight.range"]].some(checkString)) ui.notifications.error("ATL: preset string error"); if ("sight.angle" in preset) preset["sight.angle"] = parseInt(preset["sight.angle"]); if ("light.angle" in preset) preset["light.angle"] = parseInt(preset["light.angle"]); - // remove preset-specific properties - delete preset.id - delete preset.name + delete preset.id; delete preset.name; console.log("ATE | apply preset", change.value, preset); - Object.entries(preset) - .forEach(([key, value]) => { - const originalValue = getProperty(originals, key); - applyOverride(key, value, originalValue); - }); + for (const [k, v] of Object.entries(preset)) { + const orig = getProperty(originals, k); + applyOverride(k, v, orig); + } } else if (updateKey.startsWith("detectionModes.")) { - // special handling for Detection Modes const parts = updateKey.split("."); if (parts.length === 3) { const [_, id, key] = parts; const detectionModes = getProperty(overrides, "detectionModes") || duplicate(getProperty(originals, "detectionModes")) || - []; - // find the existing one or create a new one - let dm = detectionModes.find(dm => dm.id === id); - if (!dm) { - dm = { id, enabled: true, range: 0 }; - detectionModes.push(dm); - } - // build fake change to handle apply + []; + let dm = detectionModes.find(d => d.id === id); + if (!dm) { dm = { id, enabled: true, range: 0 }; detectionModes.push(dm); } const fakeChange = duplicate(change); fakeChange.key = key; const result = ATL.apply(undefined, fakeChange, undefined, dm[key]); - // update if (result !== null) { dm[key] = result; - const preValue = getProperty(originals, "detectionModes") || []; - applyOverride("detectionModes", detectionModes, preValue); + const pre = getProperty(originals, "detectionModes") || []; + applyOverride("detectionModes", detectionModes, pre); } } } else { - let preValue = getProperty(overrides, updateKey) || getProperty(originals, updateKey) + const preValue = getProperty(overrides, updateKey) || getProperty(originals, updateKey); let result = ATL.apply(entity, change, originals, preValue); - if (change.key === "ATL.alpha") result = result * result + if (change.key === "ATL.alpha") result = result * result; if (result !== null) { if (updateKey === "light.animation" && typeof result === "string") { let resultTmp; - try { - resultTmp = JSON.parse(result); - } catch (e) { - // MANAGE STRANGE ERROR FROM USERS - var fixedJSON = result - - // Replace ":" with "@colon@" if it's between double-quotes - .replace(/:\s*"([^"]*)"/g, function (match, p1) { - return ': "' + p1.replace(/:/g, '@colon@') + '"'; - }) - - // Replace ":" with "@colon@" if it's between single-quotes - .replace(/:\s*'([^']*)'/g, function (match, p1) { - return ': "' + p1.replace(/:/g, '@colon@') + '"'; - }) - - // Add double-quotes around any tokens before the remaining ":" - .replace(/(['"])?([a-z0-9A-Z_]+)(['"])?\s*:/g, '"$2": ') - - // Add double-quotes around any tokens after the remaining ":" - .replace(/:\s*(['"])?([a-z0-9A-Z_]+)(['"])?/g, ':"$2"') - - // Turn "@colon@" back into ":" - .replace(/@colon@/g, ':'); - + try { resultTmp = JSON.parse(result); } + catch (e) { + const fixedJSON = result + .replace(/:\s*"([^"]*)"/g, (m, p1) => `: "${p1.replace(/:/g, "@colon@")}"`) + .replace(/:\s*'([^']*)'/g, (m, p1) => `: "${p1.replace(/:/g, "@colon@")}"`) + .replace(/(['"])?([a-z0-9A-Z_]+)(['"])?\s*:/g, '"$2": ') + .replace(/:\s*(['"])?([a-z0-9A-Z_]+)(['"])?/g, ':"$2"') + .replace(/@colon@/g, ":"); resultTmp = JSON.parse(fixedJSON); - for (const [key, value] of Object.entries(resultTmp)) { - resultTmp[key] = ATL.switchType(key, value) - } + for (const [k, v] of Object.entries(resultTmp)) resultTmp[k] = ATL.switchType(k, v); } - // update each key separately to save the original correctly - for (let [k, v] of Object.entries(resultTmp)) { + for (const [k, v] of Object.entries(resultTmp)) { const key = `${updateKey}.${k}`; - const preValue = getProperty(originals, key); - applyOverride(key, v, preValue); + const pre = getProperty(originals, key); + applyOverride(key, v, pre); } - } - else if (updateKey === "sight.visionMode") { - // do normal update + } else if (updateKey === "sight.visionMode") { applyOverride(updateKey, result, preValue); - // also update visionMode defaults const visionDefaults = CONFIG.Canvas.visionModes[result]?.vision?.defaults || {}; - for (let [k, v] of Object.entries(visionDefaults)) { + for (const [k, v] of Object.entries(visionDefaults)) { const key = `sight.${k}`; - const preValue = getProperty(originals, key); - applyOverride(key, v, preValue); - }; - } - else + const pre = getProperty(originals, key); + applyOverride(key, v, pre); + } + } else { applyOverride(updateKey, result, preValue); + } } } } - // add originals flag to the update overrides["flags.ATL.originals"] = originalDelta; overrides = flattenObject(overrides); - // figure out what changes were removed (i.e. those in originalDelta but not in overrides) const removeDelta = (key) => { const head = key.split("."); const tail = `-=${head.pop()}`; - key = ["flags", "ATL", "originals", ...head, tail].join("."); - overrides[key] = null; + const path = ["flags", "ATL", "originals", ...head, tail].join("."); + overrides[path] = null; }; - for (const [key, value] of Object.entries(flattenObject(originalDelta))) { - if (!(key in overrides)) { - overrides[key] = value; - delete overrides[`flags.ATL.originals.${key}`]; - removeDelta(key); + for (const [k, v] of Object.entries(flattenObject(originalDelta))) { + if (!(k in overrides)) { + overrides[k] = v; + delete overrides[`flags.ATL.originals.${k}`]; + removeDelta(k); } } - // update the token document console.log("ATE | Going to update token", token.document.id, overrides); await token.document.update(overrides); } } - static apply(token, change, originals, preValue) { const modes = CONST.ACTIVE_EFFECT_MODES; switch (change.mode) { - case modes.ADD: - return ATL.applyAdd(token, change, originals, preValue); - case modes.MULTIPLY: - return ATL.applyMultiply(token, change, originals, preValue); + case modes.ADD: return ATL.applyAdd(token, change, originals, preValue); + case modes.MULTIPLY: return ATL.applyMultiply(token, change, originals, preValue); case modes.OVERRIDE: case modes.CUSTOM: case modes.UPGRADE: - case modes.DOWNGRADE: - return ATL.applyOverride(token, change, originals, preValue); + case modes.DOWNGRADE: return ATL.applyOverride(token, change, originals, preValue); } } static switchType(key, value) { - let numeric = ["brightSight", "dimSight", "light.dim", "light.bright", "dim", "bright", "scale", "height", "width", "light.angle", "light.alpha", "rotation", "speed", "intensity"] - let Boolean = ["mirrorX", "mirrorY", "light.gradual", "vision"] - if (numeric.includes(key)) return parseFloat(value) - else if (Boolean.includes(key)) { - if (value === "true") return true - if (value === "false") return false - } - else return value + const numeric = ["brightSight","dimSight","light.dim","light.bright","dim","bright","scale","height","width","light.angle","light.alpha","rotation","speed","intensity"]; + const bools = ["mirrorX","mirrorY","light.gradual","vision"]; + if (numeric.includes(key)) return parseFloat(value); + if (bools.includes(key)) return value === "true" ? true : value === "false" ? false : value; + return value; } static applyAdd(token, change, originals, current) { let { key, value } = change; - key = key.slice(4) - value = ATL.switchType(key, value) - //const current = typeof getProperty(originals, key) === "number" ? getProperty(originals, key) : getProperty(token.data, key) || null; + key = key.slice(4); + value = ATL.switchType(key, value); const ct = getType(current); let update = null; - - // Handle different types of the current data switch (ct) { - case "null": - update = parseInt(value); - break; - case "string": - update = current + String(value); - break; - case "number": - if (Number.isNumeric(value)) update = current + Number(value); - break; - case "Array": - if (!current.length || (getType(value) === getType(current[0]))) update = current.concat([value]); + case "null": update = parseInt(value); break; + case "string": update = current + String(value); break; + case "number": if (Number.isNumeric(value)) update = current + Number(value); break; + case "Array": if (!current.length || (getType(value) === getType(current[0]))) update = current.concat([value]); break; } return update; } static applyMultiply(token, change, originals, current) { let { key, value } = change; - key = key.slice(4) - value = ATL.switchType(key, value) - - //const current = typeof getProperty(originals, key) === "number" ? getProperty(originals, key) : getProperty(token.data, key) || null; - if ((typeof (current) !== "number") || (typeof (value) !== "number")) return null; - const update = current * value; - return update; + key = key.slice(4); + value = ATL.switchType(key, value); + if ((typeof current !== "number") || (typeof value !== "number")) return null; + return current * value; } static applyOverride(token, change, originals, current) { let { key, value, mode } = change; - key = key.slice(4) - value = ATL.switchType(key, value) - // current = typeof getProperty(originals, key) === "number" ? getProperty(originals, key) : getProperty(token.data, key) || null; - if (mode === CONST.ACTIVE_EFFECT_MODES.UPGRADE) { - if ((typeof (current) === "number") && (current >= Number(value))) return null; - } - if (mode === CONST.ACTIVE_EFFECT_MODES.DOWNGRADE) { - if ((typeof (current) === "number") && (current < Number(value))) return null; - } + key = key.slice(4); + value = ATL.switchType(key, value); + if (mode === CONST.ACTIVE_EFFECT_MODES.UPGRADE && typeof current === "number" && current >= Number(value)) return null; + if (mode === CONST.ACTIVE_EFFECT_MODES.DOWNGRADE && typeof current === "number" && current < Number(value)) return null; if (typeof current === "number") return Number(value); return value; } } -window.ATLUpdate = ATLUpdate +window.ATLUpdate = ATLUpdate; -Hooks.on('init', ATL.init); -Hooks.on('ready', ATL.ready) -Hooks.on('getSceneControlButtons', ATL.getSceneControlButtons) +Hooks.on("init", ATL.init); +Hooks.on("ready", ATL.ready); +Hooks.on("getSceneControlButtons", ATL.getSceneControlButtons); diff --git a/src/preset-config.js b/src/preset-config.js index c2a9f65..224c0c7 100644 --- a/src/preset-config.js +++ b/src/preset-config.js @@ -1,166 +1,200 @@ -/** - * The Application used for defining a preset configuration that can be used by the `ATL.preset` - * active effect key. It can handle updating an existing preset as well as creating a new one. +/* Preset Editor (Application V2) + * Usage: + * new PresetConfig(presetObj?).render(true) + * - If presetObj is omitted → create new preset. + * - If presetObj has an id → edit/update existing preset. */ -export class PresetConfig extends FormApplication { - /** - * Create a new application to add/edit a preset. - * @param {Object} object The ATL preset, or `undefined` if creating a new one from scratch - * @param {FormApplicationOptions} options Application configuration options - */ - constructor(object = {}, options = {}) { - super(object, options); - - /** - * The token change preset - */ - this.preset = this.object; - - /** - * Whether this app is creating a new preset or not - */ - this.newMode = !this.preset.id; - - /** - * An array of form field names that were changed - */ - this.fieldsChanged = []; +export class PresetConfig extends foundry.applications.api.ApplicationV2 { + /** @param {object} preset - optional existing preset object */ + constructor(preset = {}) { + super(); + this._incomingPreset = preset; } - static get defaultOptions() { - return foundry.utils.mergeObject(super.defaultOptions, { - classes: ["sheet", "preset-config"], - title: "ATL Light Editor", - template: "modules/ATL/templates/preset-config.hbs", - width: 480, - height: "auto", - tabs: [ - { - navSelector: '.tabs[data-group="main"]', - contentSelector: "form", - initial: "appearance", - }, - { - navSelector: '.tabs[data-group="light"]', - contentSelector: '.tab[data-tab="light"]', - initial: "basic", - }, - ], - closeOnSubmit: true, - }); - } + // ---------- ApplicationV2 options ---------- + static DEFAULT_OPTIONS = { + id: "atl-preset-config", + title: "ATL Preset", + classes: ["atl", "preset-config"], + width: 520, + height: "auto", + resizable: true, + modal: true + }; + + // ---------- Data prep for render ---------- + async _prepareContext(_options) { + // Provide defaults for a new preset + const defaults = { + id: this._incomingPreset?.id ?? null, + name: this._incomingPreset?.name ?? "", + light: { + dim: getProperty(this._incomingPreset, "light.dim") ?? 40, + bright: getProperty(this._incomingPreset, "light.bright") ?? 20, + color: getProperty(this._incomingPreset, "light.color") ?? "#ffffff", + alpha: getProperty(this._incomingPreset, "light.alpha") ?? 0.5, + animation: { + type: getProperty(this._incomingPreset, "light.animation.type") ?? "torch", + speed: getProperty(this._incomingPreset, "light.animation.speed") ?? 1, + intensity: getProperty(this._incomingPreset, "light.animation.intensity") ?? 1 + } + } + }; - static savePreset(preset) { - // put all the presets into a collection - const collection = new Collection(); - let presets = game.settings.get("ATL", "presets"); - presets.forEach((p) => collection.set(p.id, p)); + // animation type options (extend as needed) + const animationTypes = [ + "torch", "pulse", "chroma", "wave", "fog", "sunburst", "dome", + "emanation", "hexa", "ghost", "energy", "roiling", "radial", + "flicker", "blackHole", "none" + ]; - // add or update in collection - if (!preset.id) preset.id = foundry.utils.randomID(); - collection.set(preset.id, preset); + return { + preset: defaults, + isEditing: !!defaults.id, + animationTypes + }; + } - // save collection - presets = collection.toJSON(); - game.settings.set("ATL", "presets", presets); + // ---------- Render HTML ---------- + async _renderHTML(context, _options) { + const p = context.preset; + const types = context.animationTypes.map(t => + `` + ).join(""); + + return ` +
+
+ + +
+ +
+ Light + +
+ + + + + + + + + +
+
+ + +
`; } - getData(options) { - const gridUnits = game.system.gridUnits; + // ---------- Inject + listeners ---------- + async _replaceHTML(elementOrHtml, htmlOrElement, _options) { + // Normalize param order (some builds pass html first, element second) + let target = elementOrHtml; + let content = htmlOrElement; + const isEl = (n) => n && typeof n === "object" && n.nodeType === 1; + if (typeof target === "string" && isEl(content)) [target, content] = [content, target]; + if (!isEl(target)) target = this.element; + if (!target) return; + + if (typeof content === "string") target.innerHTML = content; + else if (isEl(content)) target.replaceChildren(content); + else target.innerHTML = String(content ?? ""); + + const root = target; + + // Buttons + root.querySelector('[data-action="save"]')?.addEventListener("click", () => this._onSave(root)); + root.querySelector('[data-action="cancel"]')?.addEventListener("click", () => this.close()); + root.querySelector('[data-action="delete"]')?.addEventListener("click", () => this._onDelete()); + } + + // ---------- Helpers ---------- + _readForm(root) { + const val = (sel) => root.querySelector(sel)?.value ?? ""; + const num = (sel, fallback = 0) => { + const v = parseFloat(val(sel)); + return Number.isFinite(v) ? v : fallback; + }; - // prepare Preset data - const preset = foundry.utils.deepClone(this.object); + const name = String(val('input[name="name"]')).trim(); return { - object: preset, - gridUnits: gridUnits || game.i18n.localize("GridUnits"), - colorationTechniques: AdaptiveLightingShader.SHADER_TECHNIQUES, - visionModes: Object.values(CONFIG.Canvas.visionModes).filter((f) => f.tokenConfig), - lightAnimations: Object.entries(CONFIG.Canvas.lightAnimations).reduce( - (obj, e) => { - obj[e[0]] = game.i18n.localize(e[1].label); - return obj; - }, - { "": game.i18n.localize("None") } - ), - scale: Math.abs(this.object.texture?.scaleX || 1), + // Keep existing id if editing; generate if new + id: this._incomingPreset?.id ?? randomID(10), + name, + light: { + dim: num('input[name="light.dim"]', 0), + bright: num('input[name="light.bright"]', 0), + color: String(val('input[name="light.color"]') || "#ffffff"), + alpha: num('input[name="light.alpha"]', 0.5), + animation: { + type: String(val('select[name="light.animation.type"]') || "none"), + speed: num('input[name="light.animation.speed"]', 1), + intensity: num('input[name="light.animation.intensity"]', 1) + } + } }; } - _getSubmitData(updateData = {}) { - const formData = super._getSubmitData(updateData); + async _onSave(root) { + const data = this._readForm(root); - // Mirror token scale - if ("scale" in formData) { - formData["texture.scaleX"] = formData.scale * (formData.mirrorX ? -1 : 1); - formData["texture.scaleY"] = formData.scale * (formData.mirrorY ? -1 : 1); - } - ["scale", "mirrorX", "mirrorY"].forEach((k) => delete formData[k]); - if (this.fieldsChanged.includes("scale")) this.fieldsChanged.push("texture.scaleX", "texture.scaleY"); - - // Set default name if creating a new preset with no name - if (this.newMode && !formData.name) { - const presets = game.settings.get("ATL", "presets"); - const count = presets?.length; - formData.name = `New Preset (${count + 1})`; + if (!data.name) { + ui.notifications.warn("ATL: Please give the preset a name."); + return; } - // Remove name change if updating a preset and trying to clear the name - if (!this.newMode && "name" in formData && !formData.name) delete formData.name; + // Load current presets + const presets = await game.settings.get("ATL", "presets") ?? []; - return formData; - } + // If editing, replace by id; otherwise push + const idx = presets.findIndex(p => p.id === data.id); + if (idx >= 0) presets[idx] = data; + else presets.push(data); - async _onChangeInput(event) { - super._onChangeInput(event); - - // save the field's name that was changed - const el = event.target; - if (el.name) this.fieldsChanged.push(el.name); - // colorPicker has matching name in the dataset - else if (el.dataset.edit) this.fieldsChanged.push(el.dataset.edit); - } - - async _updateObject(event, formData) { - console.log("ATL |", "_updateObject called with formData:", formData); - - // apply the changes to the original preset - Object.entries(formData) - .filter(([k, _]) => this.fieldsChanged.includes(k)) - .forEach(([k, v]) => { - if (v === "" || v === null) this._clearProperty(this.preset, k); - else foundry.utils.setProperty(this.preset, k, v); - }); - console.log("updated preset:", this.preset); - - PresetConfig.savePreset(this.preset); + await game.settings.set("ATL", "presets", presets); + ui.notifications.info("ATL: Preset saved."); + this.close(); } - _clearProperty(object, key) { - let target = object; - let cleared = false; - let parts; - - // Convert the key to an object reference if it contains dot notation - if (key.indexOf(".") !== -1) { - parts = key.split("."); - key = parts.pop(); - target = parts.reduce((o, i) => o[i], object); - } + async _onDelete() { + const currentId = this._incomingPreset?.id; + if (!currentId) return this.close(); - // Update the target - if (target && target.hasOwnProperty(key)) { - cleared = true; - delete target[key]; - // recursivly call to remove empty objects - if (parts) { - const remainingKey = parts.join("."); - if (object[remainingKey] && isEmpty(object[remainingKey])) - this._clearProperty(object, remainingKey); - } + const confirmed = await Dialog.confirm({ + title: "Delete Preset", + content: `

Are you sure you want to delete this preset?

` + }); + if (!confirmed) return; + + const presets = await game.settings.get("ATL", "presets") ?? []; + const idx = presets.findIndex(p => p.id === currentId); + if (idx >= 0) { + presets.splice(idx, 1); + await game.settings.set("ATL", "presets", presets); + ui.notifications.info("ATL: Preset deleted."); } - - // Return changed status - return cleared; + this.close(); } } + +/** Utility: Foundry-style random id (10 chars) if not globally present */ +function randomID(length = 16) { + const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + let s = ""; + for (let i = 0; i < length; i++) s += chars[Math.floor(Math.random() * chars.length)]; + return s; +} diff --git a/src/updateManager.js b/src/updateManager.js index 47f6bc1..cc2e9e4 100644 --- a/src/updateManager.js +++ b/src/updateManager.js @@ -17,37 +17,49 @@ export class ATLUpdate { } } } - static lightAlphaUpdate() { - let presets = duplicate(game.settings.get("ATL", "presets")) - for (let preset of presets) { - if (!!preset.colorIntensity) { - preset.lightAlpha = preset.colorIntensity - delete preset.colorIntensity - } + + static async lightAlphaUpdate() { + const presets = duplicate(game.settings.get("ATL", "presets")); + + // migrate each preset colorIntensity -> lightAlpha + for (const preset of presets) { + if (preset?.colorIntensity !== undefined) { + preset.lightAlpha = preset.colorIntensity; + delete preset.colorIntensity; } - new Dialog({ - title: "ATL Preset Update", - content: `Do you wish to mass auto-update your ATL presets, one time choice.
- Changes: internal change of "colorIntensity" to "lightAlpha"`, - buttons: { - one: { - label: "Yes", - callback: async () => { - await game.settings.set("ATL", "presets", presets) - await game.settings.set("ATL", "conversion", "0.2.15") - } - }, - two: { - label: "No, I'll update myself", - callback: async () => { - await game.settings.set("ATL", "conversion", "0.2.15") + } - } - } - } - }).render(true) + // Application V2 confirmation dialog (with v1 fallback) + const useV2 = foundry?.applications?.api?.DialogV2?.confirm; + let confirmed = false; + + if (useV2) { + confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { title: "ATL Preset Update" }, + content: `

Do you wish to mass auto-update your ATL presets? (one-time choice)

+

Changes: internal rename of colorIntensity to lightAlpha.

`, + yes: { label: "Yes" }, + no: { label: "No, I'll update myself" }, + defaultYes: true + }); + } else { + // Fallback for older core versions + confirmed = await Dialog.confirm({ + title: "ATL Preset Update", + content: `Do you wish to mass auto-update your ATL presets? (one-time choice)
+ Changes: internal rename of "colorIntensity" to "lightAlpha".` + }); + } + + if (confirmed) { + await game.settings.set("ATL", "presets", presets); + await game.settings.set("ATL", "conversion", "0.2.15"); + } else { + await game.settings.set("ATL", "conversion", "0.2.15"); + } } + static async v9UpdateActor(actor) { if (!actor.data.token.actorLink) return if (actor.getFlag("ATL", "conversion") >= 3.0) return diff --git a/styles/ATL.css b/styles/ATL.css index 0a38357..8192dd1 100644 --- a/styles/ATL.css +++ b/styles/ATL.css @@ -1,4 +1,114 @@ +/* =================================== + ATL Shared +=================================== */ .preset-config nav.sheet-tabs.secondary-tabs { - margin-top: -4px; - border-top: none; + margin-top: -4px; + border-top: none; +} + +.atl label { + font-weight: 600; + margin-bottom: 4px; + display: block; +} + +/* =================================== + Preset Selector +=================================== */ +.atl.preset-selector .form-group { + padding: 12px 16px; +} + +.atl.preset-selector select[name="presets"] { + width: 100%; + padding: 10px 12px; + border-radius: 8px; + line-height: 1.3; + min-height: 40px; +} + +/* Footer buttons */ +.atl.preset-selector .sheet-footer { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 8px 12px 12px; +} + +.atl.preset-selector .sheet-footer button { + padding: 8px 12px; + border-radius: 8px; + line-height: 1.2; + display: inline-flex; + align-items: center; + gap: 6px; + min-height: 36px; +} + +/* Optional breathing room */ +.atl.preset-selector .atl-preset-selector { + padding-bottom: 4px; +} + +/* =================================== + Preset Config (Editor) +=================================== */ +.atl.preset-config .atl-preset-editor { + padding: 12px 16px; +} + +.atl.preset-config .form-group { + margin-bottom: 12px; +} + +.atl.preset-config input[type="text"], +.atl.preset-config input[type="number"], +.atl.preset-config input[type="color"], +.atl.preset-config select { + width: 100%; + padding: 8px 10px; + border-radius: 6px; + line-height: 1.3; + min-height: 34px; +} + +.atl.preset-config fieldset { + border: 1px solid var(--color-border-light-primary, #888); + border-radius: 8px; + margin-top: 8px; +} + +.atl.preset-config legend { + font-weight: 600; + padding: 0 6px; +} + +.atl.preset-config .form-fields { + margin-top: 8px; +} + +.atl.preset-config .sheet-footer { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 12px 16px; +} + +.atl.preset-config .sheet-footer button { + padding: 8px 12px; + border-radius: 8px; + display: inline-flex; + align-items: center; + gap: 6px; + min-height: 36px; +} + +.atl.preset-config .sheet-footer button.save { + background: var(--color-bg-btn-save, #4caf50); + color: white; +} + +.atl.preset-config .sheet-footer button.danger { + background: var(--color-bg-btn-danger, #e53935); + color: white; }