From 3b25d8a9c467f42d70387354b48705f4fb778cae Mon Sep 17 00:00:00 2001 From: Vali Date: Tue, 26 May 2020 17:02:17 +0200 Subject: [PATCH] v0.4.0 --- CHANGELOG.md | 20 ++ README.md | 10 +- dist/css/mess.css | 14 +- dist/module.json | 4 +- dist/scripts/bundles/0.67b6.js | 6 - dist/scripts/bundles/0.67b6.js.map | 1 - dist/scripts/bundles/add-scrolling.1c30.js | 6 - .../scripts/bundles/add-scrolling.1c30.js.map | 1 - .../scripts/bundles/change-placeables.9f20.js | 6 - .../bundles/change-placeables.9f20.js.map | 1 - dist/scripts/bundles/draggable-items.a966.js | 6 - .../bundles/draggable-items.a966.js.map | 1 - dist/scripts/bundles/item-sort-btns.1907.js | 6 - .../bundles/item-sort-btns.1907.js.map | 1 - dist/scripts/bundles/modify-rolling.4bca.js | 6 - .../bundles/modify-rolling.4bca.js.map | 1 - dist/scripts/bundles/modify-templates.26bb.js | 6 - .../bundles/modify-templates.26bb.js.map | 1 - .../bundles/prepared-spell-tracker.dee3.js | 6 - .../prepared-spell-tracker.dee3.js.map | 1 - dist/scripts/bundles/welcome-screen.89ea.js | 6 + .../bundles/welcome-screen.89ea.js.map | 1 + dist/scripts/index.js | 48 ++- dist/scripts/index.js.map | 2 +- dist/templates/roll-card.html | 2 +- dist/templates/welcome-screen.html | 41 +++ img/roll-toggles.png | Bin 40256 -> 48033 bytes package.json | 2 +- src/css/mess.less | 17 +- src/module.json | 4 +- src/scripts/change-placeables.js | 2 +- src/scripts/index.js | 142 ++++---- src/scripts/modify-rolling.js | 89 ++--- src/scripts/modify-templates.js | 102 +++++- src/scripts/rolls/apply-dmg.js | 11 + src/scripts/rolls/controls.js | 76 +++++ src/scripts/rolls/dice.js | 302 +++++++++++++++++ src/scripts/rolls/index.js | 12 + src/scripts/rolls/modify-rolling.js | 314 ++++++++++++++++++ src/scripts/settings.js | 23 +- src/scripts/welcome-screen.js | 43 +++ src/templates/roll-card.html | 2 +- src/templates/welcome-screen.html | 41 +++ 43 files changed, 1140 insertions(+), 246 deletions(-) delete mode 100644 dist/scripts/bundles/0.67b6.js delete mode 100644 dist/scripts/bundles/0.67b6.js.map delete mode 100644 dist/scripts/bundles/add-scrolling.1c30.js delete mode 100644 dist/scripts/bundles/add-scrolling.1c30.js.map delete mode 100644 dist/scripts/bundles/change-placeables.9f20.js delete mode 100644 dist/scripts/bundles/change-placeables.9f20.js.map delete mode 100644 dist/scripts/bundles/draggable-items.a966.js delete mode 100644 dist/scripts/bundles/draggable-items.a966.js.map delete mode 100644 dist/scripts/bundles/item-sort-btns.1907.js delete mode 100644 dist/scripts/bundles/item-sort-btns.1907.js.map delete mode 100644 dist/scripts/bundles/modify-rolling.4bca.js delete mode 100644 dist/scripts/bundles/modify-rolling.4bca.js.map delete mode 100644 dist/scripts/bundles/modify-templates.26bb.js delete mode 100644 dist/scripts/bundles/modify-templates.26bb.js.map delete mode 100644 dist/scripts/bundles/prepared-spell-tracker.dee3.js delete mode 100644 dist/scripts/bundles/prepared-spell-tracker.dee3.js.map create mode 100644 dist/scripts/bundles/welcome-screen.89ea.js create mode 100644 dist/scripts/bundles/welcome-screen.89ea.js.map create mode 100644 dist/templates/welcome-screen.html create mode 100644 src/scripts/rolls/apply-dmg.js create mode 100644 src/scripts/rolls/controls.js create mode 100644 src/scripts/rolls/dice.js create mode 100644 src/scripts/rolls/index.js create mode 100644 src/scripts/rolls/modify-rolling.js create mode 100644 src/scripts/welcome-screen.js create mode 100644 src/templates/welcome-screen.html diff --git a/CHANGELOG.md b/CHANGELOG.md index e50e5d8..ad53e9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +# v0.4 + + # v0.3.3 * Now using the proper japanese localization code.. diff --git a/README.md b/README.md index 744773e..367f00c 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Go to the [GitHub's issue board](https://github.com/Moerill/Mess/issues) and che ### Scaling and animated template textures ![video templates](img/animated_templates.gif) Tired of FVTTs tiling for template textures? Then this feature is perfect for you! When this feature is enabled template textures are scaled up and rotated to match the template. This also allows for the usage of video files as template textures! Nice! (For all textures the same rules as for all FVTT image and video files do apply) +This also automatically activates [Automatic Ability template texture](#ability-template-textures) when you're using the DnD5e system. ### Momentum based preview snapping ![preview snapping](img/preview_snapping.gif) @@ -62,7 +63,7 @@ To make dragging around more beautiful! Letting the placeable always snap, makes ## DnD5e specific ### Rolling and targeting change -This is a big one and encompasses a variety of features. But since these features are intertwined to some extent you can only dis-/enable all of them together! (GM sets these for all users) +This is a big one and encompasses a variety of features. #### Custom attack and damage roll chat cards ![Attacking](img/attacking.gif) @@ -75,13 +76,14 @@ The flavor text (in the example ``The cat swipes at Badger lazily with a clawed #### Autoroll and Advantage toggle ![Roll toggles](img/roll-toggles.png) -This feature also adds options to toggle between *(dis-)advantage* and *normal* rolls. This will get applied at the time you click on the *to hit* button, not beforehand! (At the moment this is not applied to skill/ability checks or saving throws, but this will come soon™). +The roll change also adds options to toggle between *(dis-)advantage* and *normal* rolls, by clicking on the D20 above the chats roll mode selector. Right click will cycle through in the opposite direction. This will get applied at the time you click on the *to hit* button, not beforehand! This also adds a selection to choose if *to hit* or *damage* rolls should be rolled automatically on *attack card* creation. -#### Ability template textures +### Ability template textures ![Auto Template](img/auto_template.gif) Want to be cool and really show a fireball each time you cast it, instead of the blank template for targeting? -This feature lets you specify textures for your templates automatically created by using a feature or spell. It adds to the item sheet a field to select an image or video file as texture. If no file is specified the module will automatically try to select a file depending on the settings set or blank if none found. +This feature lets you specify textures for your templates automatically created by using a feature or spell. It adds to the item sheet a field to select an image or video file as texture. If no file is specified the module will automatically try to select a file depending on the settings set or blank if none found. +**Important** This feature gets automatically activated when you activate [Scaling and animated template textures](#scaling-and-animated-template-textures) and are using the DnD5e system. #### Auto targeting with ability templates This awesome template gets created when you cast a spell, but you still have to manually specify the tokens as targets? Uff! But i'm here to help: This feature automatically targets the tokens inside of your placed template. diff --git a/dist/css/mess.css b/dist/css/mess.css index 0c48de4..8df02ec 100644 --- a/dist/css/mess.css +++ b/dist/css/mess.css @@ -18,6 +18,7 @@ height: 1.8em; flex: 1 100%; display: flex; + margin: 0.2em 0 0.15em 0; flex-flow: row nowrap; justify-content: space-between; } @@ -38,7 +39,7 @@ justify-content: center; flex-flow: row nowrap; align-items: center; - opacity: 0.8; + opacity: 1; } #chat-controls .mess-roll-control .mess-adv-selector a i { font-size: 1.2em; @@ -59,17 +60,15 @@ position: relative; left: -0.1em; font-size: 1em; - filter: brightness(0.6); + filter: brightness(0.5); transform: rotate(30deg); z-index: 0; - color: var(--color-bad); } #chat-controls .mess-roll-control .mess-adv-selector .mess-dis-btn i:nth-child(1) { font-size: 1em; - filter: brightness(0.6); + filter: brightness(0.5); transform: rotate(45deg); z-index: 0; - color: var(--color-good); position: relative; left: 0.1em; } @@ -126,6 +125,7 @@ } #chat-log .mess-attack-card .mess-dice-result span { font-weight: bold; + font-size: 1.4em; } #chat-log .mess-attack-card .mess-dice-result .crit { color: var(--color-success); @@ -202,8 +202,8 @@ text-shadow: 0 0 10px red; cursor: pointer; } -.dnd5e.actor .ability-mod:hover, -.dnd5e.actor .ability-save:hover { +.dark-mode .dnd5e.actor .ability-mod:hover, +.dark-mode .dnd5e.actor .ability-save:hover { color: #3F88E6; text-shadow: 0 0 10px #3F88E6; } diff --git a/dist/module.json b/dist/module.json index cd30320..d94ee5e 100644 --- a/dist/module.json +++ b/dist/module.json @@ -2,7 +2,7 @@ "name": "mess", "title": "Mess - Moerills enhancing super-suit(e)", "description": "This module is a mix of QoL changes, enhancements for my game and stuff i play around with. For a complete feature list check the URL down below.", - "version": "0.3.3", + "version": "0.4.0", "minimumCoreVersion": "0.5.7", "compatibleCoreVersion": "0.5.7", "author": "Moerill", @@ -28,5 +28,5 @@ ], "manifest": "https://raw.githubusercontent.com/Moerill/mess/master/src/module.json", "url": "https://github.com/Moerill/mess", - "download": "https://github.com/Moerill/mess/releases/download/v0.3.3/mess.zip" + "download": "https://github.com/Moerill/mess/releases/download/v0.4.0/mess.zip" } \ No newline at end of file diff --git a/dist/scripts/bundles/0.67b6.js b/dist/scripts/bundles/0.67b6.js deleted file mode 100644 index 61caa6b..0000000 --- a/dist/scripts/bundles/0.67b6.js +++ /dev/null @@ -1,6 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{"./src/scripts/settings.js": -/*!*********************************!*\ - !*** ./src/scripts/settings.js ***! - \*********************************/ -/*! exports provided: MessSettings */function(e,t,s){"use strict";s.r(t),s.d(t,"MessSettings",(function(){return a}));class a extends FormApplication{static init(){const e="dnd5e"===game.system.id;game.settings.register("mess","actor-item-sort",{name:"Activate item sort button.",hint:"Adds a button to actor sheets for sorting all items of that category alphabetically.",scope:"user",config:e,default:e,type:Boolean}),game.settings.register("mess","better-draggable",{name:"Activate better drag'n'drop workflow.",scope:"user",config:!1,default:e,type:Boolean}),game.settings.register("mess","prepared-spell-tracker",{name:"Activate prepared spell tracker",hint:"Adds a tracker to the spellbook tab, providing a way to track the allowed maximum of prepared spells.",scope:"user",config:e,default:e,type:Boolean}),game.settings.register("mess","add-scrolling",{name:"Activating numerical field scrolling.",hint:"Lets you in-/decrease numerical fields in the Actor sheet using the mouse wheel when focused.",scope:"user",config:e,default:e,type:Boolean}),game.settings.register("mess","modify-rolling",{name:"Alternative Rolling.",hint:"Changes the way rolling is displayed and executed for DnD5e. Reload for all connected clients is required for this to take effect if changed!",scope:"world",config:e,default:e,type:Boolean}),game.settings.register("mess","modify-templates",{name:"Activate modified templates.",hint:"Makes templates texture fill scaling instead of tiling and does allow the usage of videos as texture. Reload for all connected clients is required for this to take effect if changed!",scope:"world",config:!0,default:!0,type:Boolean}),game.settings.register("mess","change-placeables",{name:"Activate placeables changes.",hint:"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!",scope:"world",config:!0,default:!0,type:Boolean}),e&&game.settings.registerMenu("mess","templateTexture",{name:"Mess template texture configurator for DnD abilities.",label:"Configure template textures",icon:"fas fa-mug-hot",type:a,restricted:!0}),game.settings.register("mess","templateTexture",{name:"Activate placeables changes.",hint:"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!",scope:"world",default:!0,type:Object})}static get defaultOptions(){return{...super.defaultOptions,template:"modules/mess/templates/settings.html",height:800,width:400}}constructor(e={},t){super(e,t),this.object=game.settings.get("mess","templateTexture")}getData(){let e=super.getData();return e.dmgTypes=CONFIG.DND5E.damageTypes,e.templateTypes=CONFIG.templateTypes,e}activateListeners(e){super.activateListeners(e)}_updateObject(e,t){game.settings.set("mess","templateTexture",mergeObject({},t))}}}}]); -//# sourceMappingURL=0.67b6.js.map \ No newline at end of file diff --git a/dist/scripts/bundles/0.67b6.js.map b/dist/scripts/bundles/0.67b6.js.map deleted file mode 100644 index 0d899dc..0000000 --- a/dist/scripts/bundles/0.67b6.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/scripts/settings.js"],"names":["MessSettings","FormApplication","isDnD","game","system","id","settings","register","name","hint","scope","config","default","type","Boolean","registerMenu","label","icon","restricted","Object","super","defaultOptions","template","height","width","object","options","this","get","data","getData","dmgTypes","CONFIG","DND5E","damageTypes","templateTypes","html","activateListeners","ev","formData","set","mergeObject"],"mappings":";;;;kEAAA,oDAAO,MAAMA,UAAqBC,gBACjC,cACC,MAAMC,EAA2B,UAAnBC,KAAKC,OAAOC,GAE1BF,KAAKG,SAASC,SAAS,OAAQ,kBAAmB,CACjDC,KAAM,6BACNC,KAAM,uFACNC,MAAO,OACPC,OAAQT,EACRU,QAASV,EACTW,KAAMC,UAGPX,KAAKG,SAASC,SAAS,OAAQ,mBAAoB,CAClDC,KAAM,wCACNE,MAAO,OACPC,QAAQ,EACRC,QAASV,EACTW,KAAMC,UAGPX,KAAKG,SAASC,SAAS,OAAQ,yBAA0B,CACxDC,KAAM,kCACNC,KAAM,wGACNC,MAAO,OACPC,OAAQT,EACRU,QAASV,EACTW,KAAMC,UAGPX,KAAKG,SAASC,SAAS,OAAQ,gBAAiB,CAC/CC,KAAM,wCACNC,KAAM,gGACNC,MAAO,OACPC,OAAQT,EACRU,QAASV,EACTW,KAAMC,UAGPX,KAAKG,SAASC,SAAS,OAAQ,iBAAkB,CAChDC,KAAM,uBACNC,KAAM,gJACNC,MAAO,QACPC,OAAQT,EACRU,QAASV,EACTW,KAAMC,UAGPX,KAAKG,SAASC,SAAS,OAAQ,mBAAoB,CAClDC,KAAM,+BACNC,KAAM,yLACNC,MAAO,QACPC,QAAQ,EACRC,SAAS,EACTC,KAAMC,UAGPX,KAAKG,SAASC,SAAS,OAAQ,oBAAqB,CACnDC,KAAM,+BACNC,KAAM,yJACNC,MAAO,QACPC,QAAQ,EACRC,SAAS,EACTC,KAAMC,UAEHZ,GACHC,KAAKG,SAASS,aAAa,OAAQ,kBAAmB,CACrDP,KAAM,wDACNQ,MAAO,8BAEPC,KAAM,iBACNJ,KAAMb,EACNkB,YAAY,IAGdf,KAAKG,SAASC,SAAS,OAAQ,kBAAmB,CACjDC,KAAM,+BACNC,KAAM,yJACNC,MAAO,QACPE,SAAS,EACTC,KAAMM,SAIR,4BACC,MAAO,IACHC,MAAMC,eACTC,SAAU,uCACVC,OAAQ,IACRC,MAAO,KAIT,YAAYC,EAAS,GAAIC,GACxBN,MAAMK,EAAQC,GACdC,KAAKF,OAAStB,KAAKG,SAASsB,IAAI,OAAQ,mBAGzC,UACC,IAAIC,EAAOT,MAAMU,UAGjB,OAFAD,EAAKE,SAAWC,OAAOC,MAAMC,YAC7BL,EAAKM,cAAgBH,OAAOG,cACrBN,EAGR,kBAAkBO,GACjBhB,MAAMiB,kBAAkBD,GAGzB,cAAcE,EAAIC,GACjBpC,KAAKG,SAASkC,IAAI,OAAQ,kBAAmBC,YAAY,GAAIF","file":"bundles/0.67b6.js","sourcesContent":["export class MessSettings extends FormApplication {\n\tstatic init() {\n\t\tconst isDnD = game.system.id === 'dnd5e';\n\t\t\n\t\tgame.settings.register('mess', 'actor-item-sort', {\n\t\t\tname: \"Activate item sort button.\",\n\t\t\thint: \"Adds a button to actor sheets for sorting all items of that category alphabetically.\",\n\t\t\tscope: \"user\",\n\t\t\tconfig: isDnD,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t})\n\t\n\t\tgame.settings.register('mess', 'better-draggable', {\n\t\t\tname: \"Activate better drag'n'drop workflow.\",\n\t\t\tscope: \"user\",\n\t\t\tconfig: false,// Change if implemented\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t})\n\t\n\t\tgame.settings.register('mess', 'prepared-spell-tracker', {\n\t\t\tname: \"Activate prepared spell tracker\",\n\t\t\thint: \"Adds a tracker to the spellbook tab, providing a way to track the allowed maximum of prepared spells.\",\n\t\t\tscope: \"user\",\n\t\t\tconfig: isDnD,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t})\n\t\n\t\tgame.settings.register('mess', 'add-scrolling', {\n\t\t\tname: \"Activating numerical field scrolling.\",\n\t\t\thint: \"Lets you in-/decrease numerical fields in the Actor sheet using the mouse wheel when focused.\",\n\t\t\tscope: \"user\",\n\t\t\tconfig: isDnD,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t});\n\t\n\t\tgame.settings.register('mess', 'modify-rolling', {\n\t\t\tname: \"Alternative Rolling.\",\n\t\t\thint: \"Changes the way rolling is displayed and executed for DnD5e. Reload for all connected clients is required for this to take effect if changed!\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: isDnD,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t});\n\t\n\t\tgame.settings.register('mess', 'modify-templates', {\n\t\t\tname: \"Activate modified templates.\",\n\t\t\thint: \"Makes templates texture fill scaling instead of tiling and does allow the usage of videos as texture. Reload for all connected clients is required for this to take effect if changed!\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: true,\n\t\t\tdefault: true,\n\t\t\ttype: Boolean\n\t\t});\n\t\t\n\t\tgame.settings.register('mess', 'change-placeables', {\n\t\t\tname: \"Activate placeables changes.\",\n\t\t\thint: \"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: true,\n\t\t\tdefault: true,\n\t\t\ttype: Boolean\n\t\t});\t\n\t\tif (isDnD)\n\t\t\tgame.settings.registerMenu('mess', 'templateTexture', {\n\t\t\t\tname: \"Mess template texture configurator for DnD abilities.\",\n\t\t\t\tlabel: \"Configure template textures\",\n\t\t\t\t// hint: \"!\",\n\t\t\t\ticon: \"fas fa-mug-hot\",\n\t\t\t\ttype: MessSettings,\n\t\t\t\trestricted: true\n\t\t\t});\n\n\t\tgame.settings.register('mess', 'templateTexture', {\n\t\t\tname: \"Activate placeables changes.\",\n\t\t\thint: \"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!\",\n\t\t\tscope: \"world\",\n\t\t\tdefault: true,\n\t\t\ttype: Object\n\t\t});\t\n\t}\n\n\tstatic get defaultOptions() {\n\t\treturn {\n\t\t\t...super.defaultOptions,\n\t\t\ttemplate: \"modules/mess/templates/settings.html\",\n\t\t\theight: 800,\n\t\t\twidth: 400\n\t\t}\n\t}\n\n\tconstructor(object = {}, options) {\n\t\tsuper(object, options);\n\t\tthis.object = game.settings.get('mess', 'templateTexture');\n\t}\n\n\tgetData() {\n\t\tlet data = super.getData();\n\t\tdata.dmgTypes = CONFIG.DND5E.damageTypes;\n\t\tdata.templateTypes = CONFIG.templateTypes;\n\t\treturn data;\n\t}\n\n\tactivateListeners(html) {\n\t\tsuper.activateListeners(html);\n\t}\n\n\t_updateObject(ev, formData) {\n\t\tgame.settings.set('mess', 'templateTexture', mergeObject({}, formData))\n\t}\n\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/bundles/add-scrolling.1c30.js b/dist/scripts/bundles/add-scrolling.1c30.js deleted file mode 100644 index df2708e..0000000 --- a/dist/scripts/bundles/add-scrolling.1c30.js +++ /dev/null @@ -1,6 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["add-scrolling"],{"./src/scripts/add-scrolling.js": -/*!**************************************!*\ - !*** ./src/scripts/add-scrolling.js ***! - \**************************************/ -/*! exports provided: default */function(e,t,r){"use strict";async function n(){Hooks.on("renderActorSheet",(async function(e,t,r){t[0].querySelectorAll('input[data-dtype="Number"], .item-uses input').forEach(e=>{e.addEventListener("wheel",e=>{e.preventDefault(),e.stopPropagation(),e.deltaY<0&&(e.currentTarget.value=Number(e.currentTarget.value)+1),e.deltaY>0&&(e.currentTarget.value=Math.max(Number(e.currentTarget.value)-1,0)),$(e.currentTarget).change()})})}))}r.r(t),r.d(t,"default",(function(){return n}))}}]); -//# sourceMappingURL=add-scrolling.1c30.js.map \ No newline at end of file diff --git a/dist/scripts/bundles/add-scrolling.1c30.js.map b/dist/scripts/bundles/add-scrolling.1c30.js.map deleted file mode 100644 index 45f970c..0000000 --- a/dist/scripts/bundles/add-scrolling.1c30.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/scripts/add-scrolling.js"],"names":["async","addScolling","Hooks","on","app","html","data","querySelectorAll","forEach","el","addEventListener","ev","preventDefault","stopPropagation","deltaY","currentTarget","value","Number","Math","max","$","change"],"mappings":";;;;6DAAeA,eAAeC,IAC7BC,MAAMC,GAAG,oBAAoBH,eAAgBI,EAAMC,EAAMC,GACxDD,EAAK,GAAGE,iBAAiB,gDAAgDC,QAAQC,IAChFA,EAAGC,iBAAiB,QAASC,IAC5BA,EAAGC,iBACHD,EAAGE,kBACCF,EAAGG,OAAS,IACfH,EAAGI,cAAcC,MAAQC,OAAON,EAAGI,cAAcC,OAAS,GACvDL,EAAGG,OAAS,IACfH,EAAGI,cAAcC,MAAQE,KAAKC,IAAIF,OAAON,EAAGI,cAAcC,OAAS,EAAG,IAEvEI,EAAET,EAAGI,eAAeM,gBAXxB","file":"bundles/add-scrolling.1c30.js","sourcesContent":["export default async function addScolling() {\n\tHooks.on('renderActorSheet', async function (app, html, data) {\n\t\thtml[0].querySelectorAll('input[data-dtype=\"Number\"], .item-uses input').forEach(el => {\n\t\t\tel.addEventListener('wheel', ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tev.stopPropagation();\n\t\t\t\tif (ev.deltaY < 0)\n\t\t\t\t\tev.currentTarget.value = Number(ev.currentTarget.value) + 1;\n\t\t\t\tif (ev.deltaY > 0)\n\t\t\t\t\tev.currentTarget.value = Math.max(Number(ev.currentTarget.value) - 1, 0);\n\n\t\t\t\t$(ev.currentTarget).change()\n\t\t\t});\n\t\t})\n\t});\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/bundles/change-placeables.9f20.js b/dist/scripts/bundles/change-placeables.9f20.js deleted file mode 100644 index 2148e99..0000000 --- a/dist/scripts/bundles/change-placeables.9f20.js +++ /dev/null @@ -1,6 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["change-placeables"],{"./src/scripts/change-placeables.js": -/*!******************************************!*\ - !*** ./src/scripts/change-placeables.js ***! - \******************************************/ -/*! exports provided: default */function(a,t,n){"use strict";function e(a){const{clones:t,destination:n,origin:e,originalEvent:o}=a.data;canvas._onDragCanvasPan(o);const i=n.x-e.x,s=n.y-e.y;let r=!1;if(a.data.previous){const t=30,e=.8,o=a.data.previous,i=a.data.momentum||0,s=a.data.v||{x:0,y:0},c={x:n.x-o.x,y:n.y-o.y},d={x:c.x-s.x,y:c.y-s.y};a.data.momentum=d.x*d.x+d.y*d.y+i*e,r=!a.shiftKey&&a.data.momentum current momentum does have more impact\n\t\t// => more jumping around between snapping and not\n\t\t// but higher means more waiting/slow movement time for it to snap\n\t\tconst lambda = 0.8;\n\t\tconst prev = event.data.previous;\n\t\tconst prevMomentum = event.data.momentum || 0;\n\t\tconst prevV = event.data.v || {x: 0, y: 0};\n\t\tconst v = {\n\t\t\tx: destination.x - prev.x,\n\t\t\ty: destination.y - prev.y\n\t\t}\n\t\tconst momentum = {\n\t\t\tx: v.x - prevV.x,\n\t\t\ty: v.y - prevV.y\n\t\t};\n\n\t\tevent.data.momentum = (momentum.x * momentum.x + momentum.y * momentum.y) + prevMomentum * lambda;\n\t\tsnap = !event.shiftKey && event.data.momentum < momentumThreshold;\n\t}\n\n\tevent.data.previous = destination;\n\n\t// Update the position of each clone\n\tfor ( let c of clones || [] ) {\n\t\tlet dest = {x: c._original.data.x + dx, y: c._original.data.y + dy};\n\t\tif ( snap ) {\n\t\t\tdest = canvas.grid.getSnappedPosition(dest.x, dest.y, this.layer.options.gridPrecision);\n\t\t}\n\t\tc.data.x = dest.x;\n\t\tc.data.y = dest.y;\n\t\tc.refresh();\n\t}\n}\n\nexport default function changePlaceables() {\n\tPlaceableObject.prototype._onDragLeftMove = onDragLeftMove;\n\t// Change got bigger than expected, so.. complete swap it is\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/bundles/draggable-items.a966.js b/dist/scripts/bundles/draggable-items.a966.js deleted file mode 100644 index 3482813..0000000 --- a/dist/scripts/bundles/draggable-items.a966.js +++ /dev/null @@ -1,6 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["draggable-items"],{"./src/scripts/draggable-items.js": -/*!****************************************!*\ - !*** ./src/scripts/draggable-items.js ***! - \****************************************/ -/*! exports provided: default */function(n,s,t){"use strict";async function c(){}t.r(s),t.d(s,"default",(function(){return c}))}}]); -//# sourceMappingURL=draggable-items.a966.js.map \ No newline at end of file diff --git a/dist/scripts/bundles/draggable-items.a966.js.map b/dist/scripts/bundles/draggable-items.a966.js.map deleted file mode 100644 index b383cf2..0000000 --- a/dist/scripts/bundles/draggable-items.a966.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/scripts/draggable-items.js"],"names":["async","activateDraggableItems"],"mappings":";;;;6DAAeA,eAAeC,KAA9B","file":"bundles/draggable-items.a966.js","sourcesContent":["export default async function activateDraggableItems() {\n\t// TODO.. wenn ich motivation hab :grimacing:\n\t// Hooks.on('renderActorSheet', (app, html, data) => {\n\t\t\n\t// });\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/bundles/item-sort-btns.1907.js b/dist/scripts/bundles/item-sort-btns.1907.js deleted file mode 100644 index 05a3410..0000000 --- a/dist/scripts/bundles/item-sort-btns.1907.js +++ /dev/null @@ -1,6 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["item-sort-btns"],{"./src/scripts/actor-item-sort-btn.js": -/*!********************************************!*\ - !*** ./src/scripts/actor-item-sort-btn.js ***! - \********************************************/ -/*! exports provided: default */function(t,e,n){"use strict";async function s(t,e,n){if(!n.actor._id)return;e.querySelectorAll(".filter-list").forEach(t=>{const e=t.closest(".tab").dataset.tab,s=document.createElement("a");s.innerHTML='',s.classList.add("mess-sort-btn"),s.title=`Sort ${e} alphabetically.`,s.style.flex=0,s.style.margin="0 5px 0 0",s.addEventListener("click",t=>async function(t,e){const n=await fromUuid("Actor."+e);let s=t.map(t=>t.items||t.spells).flat();s.sort((function(t,e){return t.namee.name?1:0}));let r=[s.shift()],a=[];for(;s.length>0;r.push(s.shift()))a=SortingHelpers.performIntegerSort(s[0],{target:r[r.length-1],siblings:duplicate(r),sortBefore:!1});const i=a.map(t=>{let e=t.update;return e._id=t.target._id,e});n.updateEmbeddedEntity("OwnedItem",i)}(n[e],n.actor._id)),t.prepend(s)})}async function r(){Hooks.on("renderActorSheet",(t,e,n)=>{s(0,e[0],n)})}n.r(e),n.d(e,"default",(function(){return r}))}}]); -//# sourceMappingURL=item-sort-btns.1907.js.map \ No newline at end of file diff --git a/dist/scripts/bundles/item-sort-btns.1907.js.map b/dist/scripts/bundles/item-sort-btns.1907.js.map deleted file mode 100644 index 518d4f1..0000000 --- a/dist/scripts/bundles/item-sort-btns.1907.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/scripts/actor-item-sort-btn.js"],"names":["async","addAlphabeticalSorter","app","html","data","actor","_id","querySelectorAll","forEach","el","type","closest","dataset","tab","btn","document","createElement","innerHTML","classList","add","title","style","flex","margin","addEventListener","ev","lists","actorId","fromUuid","concatList","map","e","items","spells","flat","sort","a","b","name","siblings","shift","sortUpdates","length","push","SortingHelpers","performIntegerSort","target","duplicate","sortBefore","updateData","u","update","updateEmbeddedEntity","sortItemListAlphabetically","prepend","itemSortBtn","Hooks","on"],"mappings":";;;;6DAwBAA,eAAeC,EAAsBC,EAAKC,EAAMC,GAC/C,IAAKA,EAAKC,MAAMC,IAAK,OACNH,EAAKI,iBAAiB,gBAE9BC,QAAQC,IACd,MAAMC,EAAOD,EAAGE,QAAQ,QAAQC,QAAQC,IAClCC,EAAMC,SAASC,cAAc,KACnCF,EAAIG,UAAY,8BAChBH,EAAII,UAAUC,IAAI,iBAClBL,EAAIM,MAAQ,QAAQV,oBACpBI,EAAIO,MAAMC,KAAO,EACjBR,EAAIO,MAAME,OAAS,YACnBT,EAAIU,iBAAiB,QAAUC,GApCjCzB,eAA0C0B,EAAOC,GAChD,MAAMtB,QAAcuB,SAAS,SAASD,GACtC,IAAIE,EAAaH,EAAMI,IAAIC,GAAKA,EAAEC,OAASD,EAAEE,QAAQC,OACrDL,EAAWM,MAAK,SAAUC,EAAGC,GAC5B,OAAID,EAAEE,KAAOD,EAAEC,MAAc,EACzBF,EAAEE,KAAOD,EAAEC,KAAa,EACrB,KAER,IAAIC,EAAW,CAACV,EAAWW,SACvBC,EAAc,GAClB,KAAOZ,EAAWa,OAAS,EAAGH,EAASI,KAAKd,EAAWW,SACtDC,EAAeG,eAAeC,mBAAmBhB,EAAW,GAAI,CAC/DiB,OAAQP,EAASA,EAASG,OAAO,GACjCH,SAAUQ,UAAUR,GACpBS,YAAY,IAEd,MAAMC,EAAaR,EAAYX,IAAIoB,IAClC,IAAIC,EAASD,EAAEC,OAEf,OADAA,EAAO7C,IAAM4C,EAAEJ,OAAOxC,IACf6C,IAER9C,EAAM+C,qBAAqB,YAAaH,GAeDI,CAA2BjD,EAAKM,GAAON,EAAKC,MAAMC,MACxFG,EAAG6C,QAAQxC,KAIEd,eAAeuD,IAC7BC,MAAMC,GAAG,mBAAoB,CAACvD,EAAKC,EAAMC,KACxCH,EAAsBC,EAAKC,EAAK,GAAIC,KA3CtC","file":"bundles/item-sort-btns.1907.js","sourcesContent":["async function sortItemListAlphabetically(lists, actorId) {\n\tconst actor = await fromUuid(`Actor.${actorId}`);\n\tlet concatList = lists.map(e => e.items || e.spells).flat();\n\tconcatList.sort(function (a, b) {\n\t\tif (a.name < b.name) return -1;\n\t\tif (a.name > b.name) return 1;\n\t\treturn 0;\n\t});\n\tlet siblings = [concatList.shift()];\n\tlet sortUpdates = [];\n\tfor (; concatList.length > 0; siblings.push(concatList.shift())) {\n\t\tsortUpdates = (SortingHelpers.performIntegerSort(concatList[0], {\n\t\t\ttarget: siblings[siblings.length-1], \n\t\t\tsiblings: duplicate(siblings),\n\t\t\tsortBefore: false}));\t\t\n\t}\n\tconst updateData = sortUpdates.map(u => {\n\t\tlet update = u.update;\n\t\tupdate._id = u.target._id;\n\t\treturn update;\n\t})\n\tactor.updateEmbeddedEntity('OwnedItem', updateData);\n}\n\nasync function addAlphabeticalSorter(app, html, data) {\n\tif (!data.actor._id) return;\n\tconst header = html.querySelectorAll('.filter-list');\n\n\theader.forEach(el => {\n\t\tconst type = el.closest('.tab').dataset.tab;\n\t\tconst btn = document.createElement('a');\n\t\tbtn.innerHTML = '';\n\t\tbtn.classList.add('mess-sort-btn');\n\t\tbtn.title = `Sort ${type} alphabetically.`;\n\t\tbtn.style.flex = 0;\n\t\tbtn.style.margin = \"0 5px 0 0\";\n\t\tbtn.addEventListener('click',\t(ev) => sortItemListAlphabetically(data[type], data.actor._id))\n\t\tel.prepend(btn);\n\t});\n}\n\nexport default async function itemSortBtn() {\n\tHooks.on('renderActorSheet', (app, html, data) => {\n\t\taddAlphabeticalSorter(app, html[0], data);\n\t});\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/bundles/modify-rolling.4bca.js b/dist/scripts/bundles/modify-rolling.4bca.js deleted file mode 100644 index ea6e86a..0000000 --- a/dist/scripts/bundles/modify-rolling.4bca.js +++ /dev/null @@ -1,6 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["modify-rolling"],{"./src/scripts/modify-rolling.js": -/*!***************************************!*\ - !*** ./src/scripts/modify-rolling.js ***! - \***************************************/ -/*! exports provided: default */function(e,t,a){"use strict";function s(){return game.settings.get("mess",game.userId+".adv-selector")}async function n(e){return"attack"===e.currentTarget.dataset.action||"damage"===e.currentTarget.dataset.action?d(e):e.currentTarget.dataset.placeTemplate?renderTemplate(e):this._onChatCardAction(e)}async function r({actor:e,item:t}){if(!t.hasAttack)return null;const a=e.data.data,s=t.data.data,n=e.data.flags.dnd5e||{};let r=t.getRollData();const o=["@mod"];("weapon"!==t.data.type||s.proficient)&&o.push("@prof"),r.parts=o,"weapon"===t.data.type&&n.weaponCriticalThreshold&&(r.critical=parseInt(n.weaponCriticalThreshold)),["weapon","spell"].includes(t.data.type)&&n.elvenAccuracy&&["dex","int","wis","cha"].includes(t.abilityMod)&&(r.elvenAccuracy=!0),n.halflingLucky&&(r.halflingLucky=!0);const l=a.bonuses[s.actionType]||{};(s.attackBonus||l.attack)&&(r.atk=[s.attackBonus,l.attack].filterJoin(" + "),isNaN(Number(r.atk))||o.push("@atk"));let i=new Roll(r.parts.join("+"),r);r.totalModifier=i._safeEval(i.formula),r.totalModifier=r.totalModifier>=0?"+"+r.totalModifier:r.totalModifier,r.atk&&!i._formula.includes("@atk")&&(r.parts.push("@atk"),i=new Roll(r.parts.join("+"),r),r.totalModifier+="+"+r.atk);const c=document.getElementById("mess-roll-mod");return c.value&&(r.parts.push(c.value),i=new Roll(r.parts.join("+"),r),r.totalModifier+="+"+c.value),r.formula=i.formula,r.terms=i._formula,r}async function o({actor:e,item:t,spellLevel:a=null}){if(!t.hasDamage)return null;const s=e.data.data,n=t.data.data;let r=t.getRollData();if(a&&(r.item.level=a),r.parts=duplicate(n.damage.parts),n.damage.versatile&&r.parts.splice(1,0,[n.damage.versatile,"versatile"]),"spell"===t.data.type)if("cantrip"===n.scaling.mode){let a=[r.parts[0][0]];const o="character"===e.data.type?s.details.level:s.details.spellLevel;t._scaleCantripDamage(a,o,n.scaling.formula),r.parts[0][0]=a[0]}else if(a&&"level"===n.scaling.mode&&n.scaling.formula){let e=[];t._scaleSpellDamage(e,n.level,a,n.scaling.formula),e.length>0&&(e.push("upcast dice"),r.parts.push(e))}const o=s.bonuses[n.actionType]||{};o.damage&&0!==parseInt(o.damage)&&(parts[0][0]+="+@dmg",r.dmg=o.damage);for(let e of r.parts){let t=new Roll(e[0],r);CONFIG.DND5E.damageTypes[e[1]]?e[1]=game.i18n.localize("DND5E.Damage"+CONFIG.DND5E.damageTypes[e[1]]):"versatile"===e[1]&&(e[1]=game.i18n.localize("DND5E.Versatile")),e.push(t.formula)}return r}async function l(e){const t=e.currentTarget;t.disabled=!0;const a=t.closest(".chat-card"),n=a.closest(".message").dataset.messageId;if(n){const e=game.messages.get(n);if(!e.owner&&!e.isAuthor)return void ui.notifications.error("You do not own the permissions to make that roll!")}const o=CONFIG.Item.entityClass._getChatCardActor(a);if(!o.owner)return!1;const l=o.getOwnedItem(a.dataset.itemId);if(!l)return ui.notifications.error(`The requested item ${a.dataset.itemId} no longer exists on Actor ${o.name}`);let i=await r({actor:o,item:l}),c=s(),d=1,u=i.halflingLucky?"r=1":"";"advantage"===c?(d=i.elvenAccuracy?3:2,u+="kh"):"disadvantage"===c&&(d=2,u+="kl"),i.parts.unshift(`${d}d20${u}`);let m=new Roll(i.parts.join("+"),i);m.roll();let g=document.createElement("div");g.title=`${i.parts[0]}+${i.terms} = ${m.formula} = ${m.total}. Click to see rolls.`,g.classList.add("dice-roll"),g.classList.add("mess-dice-result");const p=g.appendChild(document.createElement("span"));p.innerText=m.total,g.insertAdjacentHTML("beforeend",await m.getTooltip()),g.childNodes[1].classList.add("hidden");const f=i.critical||20,h=i.fumble||1,y=m.parts[0].total;if(y>=f&&(p.classList.add("crit"),a.querySelector(".mess-chat-dmg .mess-chat-roll-type").innerHTML+=" - Crit!",a.querySelectorAll(".mess-button-dmg").forEach((e,t)=>{const a=e.dataset.formula,s=new Roll(a);s.alter(0,2),e.innerHTML=' '+s.formula,e.dataset.formula=s.formula})),y<=h&&p.classList.add("fumble"),e.currentTarget.parentNode.replaceChild(g,e.currentTarget),n){game.messages.get(n).update({content:a.parentNode.innerHTML})}}async function i(e){const t=e.currentTarget;t.disabled=!0;const a=t.closest(".chat-card"),s=a.closest(".message").dataset.messageId;if(s){const e=game.messages.get(s);if(!e.owner&&!e.isAuthor)return void ui.notifications.error("You do not own the permissions to make that roll!")}const n=t.dataset.formula;let r=new Roll(n);r.roll();let o=document.createElement("div");if(o.title=`${t.dataset.terms} = ${r.formula} = ${r.total}. Click to see rolls.`,o.classList.add("dice-roll"),o.classList.add("mess-dice-result"),o.appendChild(document.createElement("span")).innerText=r.total,o.insertAdjacentHTML("beforeend",await r.getTooltip()),o.childNodes[1].classList.add("hidden"),e.currentTarget.parentNode.replaceChild(o,e.currentTarget),s){game.messages.get(s).update({content:a.parentNode.innerHTML})}}async function c(e,t){let a=document.createElement("div");if(a.classList.add("message"),a.insertAdjacentHTML("afterbegin",t),e.hit){let e=a.querySelector(".mess-button-to-hit");e&&await l({currentTarget:e})}if(e.dmg){const e=Array.from(a.querySelectorAll(".mess-button-dmg"));for(const t of e)await i({currentTarget:t})}return a.innerHTML}async function d(e){"click"===e.type&&(e.preventDefault(),e.stopPropagation());const t=e.currentTarget;t.disabled=!0;const a=t.closest(".chat-card"),s=CONFIG.Item.entityClass._getChatCardActor(a);if(!s||!s.owner)return;const n=s.getOwnedItem(a.dataset.itemId);if(!n)return ui.notifications.error(`The requested item ${a.dataset.itemId} no longer exists on Actor ${s.name}`);let l=game.user.targets;const i=Object.keys(CONFIG.DND5E.areaTargetTypes).includes(getProperty(n,"data.data.target.type"));l.size&&!i||(l=[{data:{name:"someone",img:""}}]);const d=parseInt(a.dataset.spellLevel)||null,u={actor:s,item:n,toHit:await r({actor:s,item:n}),dmgs:await o({actor:s,item:n,spellLevel:d}),sceneId:canvas.scene.id,user:game.user.id},m=game.settings.get("mess",game.userId+".autoroll-selector");let g=game.settings.get("core","rollMode");for(const e of l){const t=await n._handleResourceConsumption({isCard:!1,isAttack:!0}),a={...u,target:e.data,flavor:n.data.data.chatFlavor.replace(/\[target\.name\]/g,e.data.name),allowed:t};let s=await renderTemplate("modules/mess/templates/attack-card.html",a);(m.hit||m.dmg)&&(s=await c(m,s));let r={user:game.user._id,type:CONST.CHAT_MESSAGE_TYPES.OTHER,content:s,speaker:{actor:n.actor._id,token:n.actor.token,alias:n.actor.name}};["gmroll","blindroll"].includes(g)&&(r.whisper=ChatMessage.getWhisperIDs("GM")),"blindroll"===g&&(r.blind=!0),ChatMessage.create(r)}t.disabled=!1}async function u(e){const t=e.currentTarget.closest(".mess-attack-card");if(t.dataset.sceneId!==canvas.scene.id)return!1;const a=t.dataset.targetId;if(!a)return!1;const s=canvas.tokens.placeables.find(e=>e.id===a);return s||!1}async function m(e){e.preventDefault(),e.stopPropagation();const t=await u(e);if(!t)return!1;t._onHoverIn()}async function g(e){e.preventDefault(),e.stopPropagation();const t=await u(e);if(!t||!t.visible)return!1;t._onHoverOut()}async function p(e){e.preventDefault(),e.stopPropagation();const t=await u(e);if(!t||!t.visible)return!1;const a=t.center;canvas.animatePan({x:a.x,y:a.y})}function f(e){const t=canvas.scene.data.grid,a=this.data.x,s=this.data.y,n=e.width>=1?.5:e.width/2,r=e.height>=1?.5:e.height/2;for(let o=n;o\n\t\t\t\n\t\t\n\t');const r=s.querySelector("button");r.style.flex="0",e._activateFilePicker(r),t[0].querySelector('[name="data.target.units"]').closest(".form-group").after(a)}async function b(e){let t=s(),a=1,n=e.halflingLucky?"r=1":"";"advantage"===t?(a=e.elvenAccuracy?3:2,n+="kh",e.title+=` (${game.i18n.localize("DND5E.Advantage")})`):"disadvantage"===t&&(a=2,n+="kl",e.title+=` (${game.i18n.localize("DND5E.Disadvantage")})`);let r=`${a}d20${n}`;e.reliableTalent&&(r=`{${a}d20${n},10}kh`),e.parts.unshift(r);const o=document.getElementById("mess-roll-mod").value;o&&e.parts.push(o);let l=new Roll(e.parts.join("+"),e);l.roll();const i=l.parts[0].total;let c={...e,tooltip:await l.getTooltip(),roll:l,crit:i>=20,fumble:i<=1};const d=await renderTemplate("modules/mess/templates/roll-card.html",c);let u={user:game.user._id,type:CONST.CHAT_MESSAGE_TYPES.OTHER,content:d,speaker:{actor:this,alias:this.name}},m=game.settings.get("core","rollMode");["gmroll","blindroll"].includes(m)&&(u.whisper=ChatMessage.getWhisperIDs("GM")),"blindroll"===m&&(u.blind=!0),ChatMessage.create(u)}async function v(e,t,a){const s=t[0].querySelectorAll(".ability-mod, .ability-name");$(s).off(),s.forEach(t=>t.addEventListener("click",(function(t){t.stopPropagation(),t.preventDefault();const a=t.currentTarget.closest(".ability").dataset.ability,s=CONFIG.DND5E.abilities[a],n=["@mod"],r={mod:e.object.data.data.abilities[a].mod},o=e.object.data.flags.dnd5e||{};o.remarkableAthlete&&DND5E.characterFlags.remarkableAthlete.abilities.includes(a)?(n.push("@proficiency"),r.proficiency=Math.ceil(.5*this.data.data.attributes.prof)):o.jackOfAllTrades&&(n.push("@proficiency"),r.proficiency=Math.floor(.5*this.data.data.attributes.prof));let l=getProperty(e.object.data.data.bonuses,"abilities.check");return l&&(n.push("@checkBonus"),r.checkBonus=l),r.parts=n,r.title=game.i18n.format("DND5E.AbilityPromptTitle",{ability:s}),b.bind(e.object)(r),!0}))),t[0].querySelectorAll(".ability-save").forEach(t=>t.addEventListener("click",(function(t){t.stopPropagation(),t.preventDefault();const a=t.currentTarget.closest(".ability").dataset.ability,s=CONFIG.DND5E.abilities[a],n=e.object.data.data.abilities[a],r=["@mod"],o={mod:n.mod};n.prof>0&&(r.push("@prof"),o.prof=n.prof);const l=getProperty(e.object.data.data.bonuses,"abilities.save");l&&(r.push("@saveBonus"),o.saveBonus=l),o.title=game.i18n.format("DND5E.SavePromptTitle",{ability:s}),o.parts=r,b.bind(e.object)(o)})));const n=t[0].querySelectorAll(".skill-name");$(n).off(),n.forEach(t=>t.addEventListener("click",(function(t){t.stopPropagation(),t.preventDefault();const a=t.currentTarget.closest(".skill").dataset.skill,s=e.object.data.data.skills[a],n=["@mod"],r={mod:s.mod+s.prof};s.bonus&&(r.skillBonus=s.bonus,n.push("@skillBonus"));s.value>=1&&e.object.getFlag("dnd5e","reliableTalent");return r.parts=n,r.title=game.i18n.format("DND5E.SkillPromptTitle",{skill:CONFIG.DND5E.skills[a]}),b.bind(e.object)(r),!1})))}async function T(){game.settings.register("mess",game.userId+".adv-selector",{name:"Mess - Advantage Selector",default:"normal",type:String,scope:"user"}),game.settings.register("mess",game.userId+".autoroll-selector",{name:"Mess - Autoroll Selector",default:{hit:!1,dmg:!1},type:Object,scope:"user"}),Hooks.on("renderChatLog",async(e,t,a)=>{const s=await async function(){const e=document.createElement("div");e.classList.add("mess-roll-control");const t=game.settings.get("mess",game.userId+".adv-selector"),a={advantage:"advantage"===t,normal:"normal"===t,disadvantage:"disadvantage"===t,...game.settings.get("mess",game.userId+".autoroll-selector")};return e.insertAdjacentHTML("afterbegin",await renderTemplate("modules/mess/templates/roll-control.html",a)),e.querySelectorAll(".mess-adv-selector a").forEach(e=>{e.addEventListener("click",(async function(e){e.preventDefault(),e.stopPropagation();const t=Array.from(e.currentTarget.parentNode.querySelectorAll("a")),a=t.findIndex(e=>e.classList.contains("mess-selected"));t[a].classList.remove("mess-selected");const s=t[(a+1)%t.length];s.classList.add("mess-selected"),game.settings.set("mess",game.userId+".adv-selector",s.name)}))}),e.querySelectorAll(".mess-autoroll-selector a").forEach(e=>{e.addEventListener("click",(async function(e){e.preventDefault(),e.stopPropagation(),e.currentTarget.classList.toggle("mess-selected");let t=game.settings.get("mess",game.userId+".autoroll-selector");t[e.currentTarget.name]=e.currentTarget.classList.contains("mess-selected"),game.settings.set("mess",game.userId+".autoroll-selector",t)}))}),e}(),n=t[0].querySelector("#chat-controls");n.insertBefore(s,n.childNodes[0])}),CONFIG.Item.entityClass.chatListeners=function(e){e.on("click",".card-buttons button",n.bind(this)),e.on("click",".item-name",this._onChatCardToggleContent.bind(this)),e.on("mouseenter",".mess-chat-target",m),e.on("mouseleave",".mess-chat-target",g),e.on("dblclick",".mess-chat-target",p),e.on("click",".mess-button-to-hit",l),e.on("click",".mess-button-dmg",i)},Hooks.on("preCreateChatMessage",async e=>{const t=document.createElement("div");t.insertAdjacentHTML("afterbegin",e.content);let a=t.querySelector('button[data-action="attack"]');a||(a=t.querySelector('button[data-action="damage"]')),a&&d({currentTarget:a})}),Hooks.on("renderItemSheet",y),Hooks.on("renderActorSheet",v),async function(){const e=await import("/systems/dnd5e/module/pixi/ability-template.js"),t=e.default||e.AbilityTemplate,a=t.fromItem;t.fromItem=function(e){const t=a.bind(this)(e);let s=e.getFlag("mess","templateTexture");if(!s&&e.hasDamage){const a=game.settings.get("mess","templateTexture")||{};s=a[e.data.data.damage.parts[0][1]]||{},s=s[t.data.t]}return s&&loadTexture(s).then(e=>{t.texture=e,t.data.texture=s,t.refresh()}),t.item=e,t};const s=t.prototype.activatePreviewListeners.toString().replace(/this\.refresh\(\)\;/,"this.refresh();\n\t\t\t\t\tthis.getTargets(this);\n\t\t\t\t");t.prototype.getTargets=h,t.prototype.isTokenInside=f,t.prototype.activatePreviewListeners=Function(`"use strict"; return ( function ${s} )`)()}()}a.r(t),a.d(t,"default",(function(){return T}))}}]); -//# sourceMappingURL=modify-rolling.4bca.js.map \ No newline at end of file diff --git a/dist/scripts/bundles/modify-rolling.4bca.js.map b/dist/scripts/bundles/modify-rolling.4bca.js.map deleted file mode 100644 index 3759847..0000000 --- a/dist/scripts/bundles/modify-rolling.4bca.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/scripts/modify-rolling.js"],"names":["getAdvantageSettings","game","settings","get","userId","async","onChatCardAction","ev","currentTarget","dataset","action","renderAttack","placeTemplate","renderTemplate","this","_onChatCardAction","getToHitData","actor","item","hasAttack","actorData","data","itemData","flags","dnd5e","rollData","getRollData","parts","type","proficient","push","weaponCriticalThreshold","critical","parseInt","includes","elvenAccuracy","abilityMod","halflingLucky","actorBonus","bonuses","actionType","attackBonus","attack","filterJoin","isNaN","Number","roll","Roll","join","totalModifier","_safeEval","formula","_formula","situationalModifier","document","getElementById","value","terms","getDmgsData","spellLevel","hasDamage","level","duplicate","damage","versatile","splice","scaling","mode","newDmgPart","lvl","details","_scaleCantripDamage","_scaleSpellDamage","length","part","CONFIG","DND5E","damageTypes","i18n","localize","rollHit","button","disabled","card","closest","messageId","message","messages","owner","isAuthor","ui","notifications","error","Item","entityClass","_getChatCardActor","getOwnedItem","itemId","name","adv","nd","mods","unshift","r","div","createElement","title","total","classList","add","span","appendChild","innerText","insertAdjacentHTML","getTooltip","childNodes","crit","fumble","d20","querySelector","innerHTML","querySelectorAll","forEach","e","idx","alter","parentNode","replaceChild","update","content","rollDmg","autoRoll","autoroll","template","hit","toHitBtn","dmg","btns","Array","from","btn","preventDefault","stopPropagation","targets","user","areaSkill","Object","keys","areaTargetTypes","getProperty","size","img","attackData","toHit","dmgs","sceneId","canvas","scene","id","rollMode","target","allowed","_handleResourceConsumption","isCard","isAttack","attackTemplateData","flavor","chatFlavor","replace","html","chatData","_id","CONST","CHAT_MESSAGE_TYPES","OTHER","speaker","token","alias","ChatMessage","getWhisperIDs","create","getTargetToken","tokenId","targetId","tokens","placeables","find","onMouseEnterTarget","_onHoverIn","onMouseLeaveTarget","visible","_onHoverOut","onDblClickTarget","pos","center","animatePan","x","y","isTokenInside","grid","templatePos","startX","width","startY","height","currGrid","shape","contains","getTargets","getEmbeddedCollection","updateTokenTargets","itemHook","app","formField","inp","dtype","object","getFlag","style","flex","_activateFilePicker","after","rollD20","diceFormula","reliableTalent","d20Mod","templateData","tooltip","actorSheetHook","abilityMods","$","off","addEventListener","abilityId","ability","label","abilities","mod","feats","remarkableAthlete","characterFlags","proficiency","Math","ceil","attributes","prof","jackOfAllTrades","floor","checkBonus","format","bind","abl","saveBonus","skills","skillId","skill","skl","bonus","modifyRolling","register","default","String","scope","Hooks","on","advSelector","advantage","normal","disadvantage","arr","currIdx","findIndex","remove","newSelected","set","toggle","createControls","controls","insertBefore","chatListeners","_onChatCardToggleContent","importedJS","import","AbilityTemplate","_originalFromItem","fromItem","path","t","loadTexture","then","tex","texture","refresh","newFun","prototype","activatePreviewListeners","toString","Function","changeAbilityTemplate"],"mappings":";;;;6DAAA,SAASA,IACR,OAAOC,KAAKC,SAASC,IAAI,OAAWF,KAAKG,OAAR,iBAyDlCC,eAAeC,EAAkBC,GAChC,MAAwC,WAApCA,EAAGC,cAAcC,QAAQC,QAEW,WAApCH,EAAGC,cAAcC,QAAQC,OADrBC,EAAaJ,GAGjBA,EAAGC,cAAcC,QAAQG,cACrBC,eAAeN,GAEhBO,KAAKC,kBAAkBR,GAG/BF,eAAeW,GAAa,MAACC,EAAK,KAAEC,IACnC,IAAKA,EAAKC,UAAW,OAAO,KAC5B,MAAMC,EAAYH,EAAMI,KAAKA,KACvBC,EAAWJ,EAAKG,KAAKA,KACrBE,EAAQN,EAAMI,KAAKE,MAAMC,OAAS,GAExC,IAAIC,EAAWP,EAAKQ,cAGpB,MAAMC,EAAQ,CAAC,SACU,WAAnBT,EAAKG,KAAKO,MAAsBN,EAASO,aAC9CF,EAAMG,KAAK,SAEZL,EAASE,MAAQA,EAGQ,WAAnBT,EAAKG,KAAKO,MAAuBL,EAAMQ,0BAC5CN,EAASO,SAAWC,SAASV,EAAMQ,0BAI/B,CAAC,SAAU,SAASG,SAAShB,EAAKG,KAAKO,OACvCL,EAAMY,eAAiB,CAAC,MAAO,MAAO,MAAO,OAAOD,SAAShB,EAAKkB,cACrEX,EAASU,eAAgB,GAKtBZ,EAAMc,gBAAgBZ,EAASY,eAAgB,GAGpD,MAAMC,EAAalB,EAAUmB,QAAQjB,EAASkB,aAAe,IACxDlB,EAASmB,aAAeH,EAAWI,UAEvCjB,EAAc,IAAI,CAACH,EAASmB,YAAaH,EAAWI,QAAQC,WAAW,OAClEC,MAAMC,OAAOpB,EAAc,OAC/BE,EAAMG,KAAK,SAIb,IAAIgB,EAAO,IAAIC,KAAKtB,EAASE,MAAMqB,KAAK,KAAMvB,GAC9CA,EAASwB,cAAgBH,EAAKI,UAAUJ,EAAKK,SAC7C1B,EAASwB,cAAgBxB,EAASwB,eAAiB,EAAI,IAAMxB,EAASwB,cAAgBxB,EAASwB,cAC3FxB,EAAc,MAAMqB,EAAKM,SAASlB,SAAS,UAC9CT,EAASE,MAAMG,KAAK,QACpBgB,EAAO,IAAIC,KAAKtB,EAASE,MAAMqB,KAAK,KAAMvB,GAC1CA,EAASwB,eAAiB,IAAIxB,EAAc,KAE7C,MAAM4B,EAAsBC,SAASC,eAAe,iBAQpD,OAPIF,EAAoBG,QACvB/B,EAASE,MAAMG,KAAKuB,EAAoBG,OACxCV,EAAO,IAAIC,KAAKtB,EAASE,MAAMqB,KAAK,KAAMvB,GAC1CA,EAASwB,eAAiB,IAAII,EAAoBG,OAEnD/B,EAAS0B,QAAUL,EAAKK,QACxB1B,EAASgC,MAAQX,EAAKM,SACf3B,EAGRpB,eAAeqD,GAAY,MAACzC,EAAK,KAAEC,EAAI,WAAEyC,EAAa,OACrD,IAAKzC,EAAK0C,UAAW,OAAO,KAC5B,MAAMxC,EAAYH,EAAMI,KAAKA,KACvBC,EAAWJ,EAAKG,KAAKA,KAC3B,IAAII,EAAWP,EAAKQ,cAQpB,GANKiC,IAAalC,EAASP,KAAK2C,MAAQF,GAExClC,EAASE,MAAQmC,UAAUxC,EAASyC,OAAOpC,OACvCL,EAASyC,OAAOC,WACnBvC,EAASE,MAAMsC,OAAO,EAAG,EAAG,CAAC3C,EAASyC,OAAOC,UAAW,cAElC,UAAnB9C,EAAKG,KAAKO,KACb,GAA8B,YAA1BN,EAAS4C,QAAQC,KAAoB,CACxC,IAAIC,EAAa,CAAC3C,EAASE,MAAM,GAAG,IACpC,MAAM0C,EAA0B,cAApBpD,EAAMI,KAAKO,KAAuBR,EAAUkD,QAAQT,MAAQzC,EAAUkD,QAAQX,WAC1FzC,EAAKqD,oBAAoBH,EAAYC,EAAK/C,EAAS4C,QAAQf,SAC3D1B,EAASE,MAAM,GAAG,GAAKyC,EAAW,QAC5B,GAAIT,GAAyC,UAA1BrC,EAAS4C,QAAQC,MAAqB7C,EAAS4C,QAAQf,QAAU,CAC1F,IAAIiB,EAAa,GACjBlD,EAAKsD,kBAAkBJ,EAAY9C,EAASuC,MAAOF,EAAYrC,EAAS4C,QAAQf,SAC5EiB,EAAWK,OAAS,IACvBL,EAAWtC,KAAK,eAChBL,EAASE,MAAMG,KAAKsC,IAKvB,MAAM9B,EAAalB,EAAUmB,QAAQjB,EAASkB,aAAe,GACzDF,EAAWyB,QAA2C,IAAjC9B,SAASK,EAAWyB,UAC5CpC,MAAM,GAAG,IAAM,QACfF,EAAc,IAAIa,EAAWyB,QAG9B,IAAK,IAAIW,KAAQjD,EAASE,MAAO,CAChC,IAAImB,EAAO,IAAIC,KAAK2B,EAAK,GAAIjD,GACbkD,OAAOC,MAAMC,YAAYH,EAAK,IAE7CA,EAAK,GAAKzE,KAAK6E,KAAKC,SAAS,eAAiBJ,OAAOC,MAAMC,YAAYH,EAAK,KACxD,cAAZA,EAAK,KACbA,EAAK,GAAKzE,KAAK6E,KAAKC,SAAS,oBAC9BL,EAAK5C,KAAKgB,EAAKK,SAGhB,OAAO1B,EAGRpB,eAAe2E,EAAQzE,GAEtB,MAAM0E,EAAS1E,EAAGC,cAClByE,EAAOC,UAAW,EAClB,MAAMC,EAAOF,EAAOG,QAAQ,cACtBC,EAAYF,EAAKC,QAAQ,YAAY3E,QAAQ4E,UAEnD,GAAIA,EAAW,CACd,MAAMC,EAAUrF,KAAKsF,SAASpF,IAAIkF,GAClC,IAAMC,EAAQE,QAASF,EAAQG,SAE9B,YADAC,GAAGC,cAAcC,MAAM,qDAKzB,MAAM3E,EAAQ0D,OAAOkB,KAAKC,YAAYC,kBAAkBZ,GACxD,IAAKlE,EAAMuE,MAAO,OAAO,EAGzB,MAAMtE,EAAOD,EAAM+E,aAAab,EAAK1E,QAAQwF,QAC7C,IAAM/E,EACL,OAAOwE,GAAGC,cAAcC,MAAM,sBAAsBT,EAAK1E,QAAQwF,oCAAoChF,EAAMiF,QAG5G,IAAIzE,QAAiBT,EAAa,CAACC,QAAOC,SACtCiF,EAAMnG,IAENoG,EAAK,EACLC,EAAO5E,EAASY,cAAgB,MAAQ,GAG/B,cAAR8D,GACJC,EAAK3E,EAASU,cAAgB,EAAI,EAClCkE,GAAQ,MAIS,iBAARF,IACTC,EAAK,EACLC,GAAQ,MAIT5E,EAASE,MAAM2E,QAAQ,GAAGF,OAAQC,KAElC,IAAIE,EAAI,IAAIxD,KAAKtB,EAASE,MAAMqB,KAAK,KAAMvB,GAC3C8E,EAAEzD,OACF,IAAI0D,EAAMlD,SAASmD,cAAc,OACjCD,EAAIE,MAAQ,GAAGjF,EAASE,MAAM,MAAMF,EAASgC,WAAW8C,EAAEpD,aAAaoD,EAAEI,6BACzEH,EAAII,UAAUC,IAAI,aAClBL,EAAII,UAAUC,IAAI,oBAClB,MAAMC,EAAON,EAAIO,YAAYzD,SAASmD,cAAc,SACpDK,EAAKE,UAAYT,EAAEI,MACnBH,EAAIS,mBAAmB,kBAAmBV,EAAEW,cAC5BV,EAAIW,WAAW,GACvBP,UAAUC,IAAI,UACtB,MAAMO,EAAO3F,EAASO,UAAY,GAC5BqF,EAAS5F,EAAS4F,QAAU,EAE5BC,EAAMf,EAAE5E,MAAM,GAAGgF,MAgBvB,GAfIW,GAAOF,IACVN,EAAKF,UAAUC,IAAI,QACnB1B,EAAKoC,cAAc,uCAAuCC,WAAa,WACvErC,EAAKsC,iBAAiB,oBAAoBC,QAAQ,CAACC,EAAGC,KACrD,MAAMzE,EAAUwE,EAAElH,QAAQ0C,QACpBoD,EAAI,IAAIxD,KAAKI,GACnBoD,EAAEsB,MAAM,EAAG,GACXF,EAAEH,UAAY,mCAAmCjB,EAAEpD,QACnDwE,EAAElH,QAAQ0C,QAAUoD,EAAEpD,WAGpBmE,GAAOD,GACVP,EAAKF,UAAUC,IAAI,UAEpBtG,EAAGC,cAAcsH,WAAWC,aAAavB,EAAKjG,EAAGC,eAC7C6E,EAAW,CACEpF,KAAKsF,SAASpF,IAAIkF,GAC1B2C,OAAO,CAACC,QAAS9C,EAAK2C,WAAWN,aAI3CnH,eAAe6H,EAAQ3H,GAEtB,MAAM0E,EAAS1E,EAAGC,cAClByE,EAAOC,UAAW,EAClB,MAAMC,EAAOF,EAAOG,QAAQ,cACtBC,EAAYF,EAAKC,QAAQ,YAAY3E,QAAQ4E,UAGnD,GAAIA,EAAW,CACd,MAAMC,EAAUrF,KAAKsF,SAASpF,IAAIkF,GAClC,IAAMC,EAAQE,QAASF,EAAQG,SAE9B,YADAC,GAAGC,cAAcC,MAAM,qDAIzB,MAAMzC,EAAU8B,EAAOxE,QAAQ0C,QAE/B,IAAIoD,EAAI,IAAIxD,KAAKI,GACjBoD,EAAEzD,OACF,IAAI0D,EAAMlD,SAASmD,cAAc,OAYjC,GAXAD,EAAIE,MAAQ,GAAGzB,EAAOxE,QAAQgD,WAAW8C,EAAEpD,aAAaoD,EAAEI,6BAC1DH,EAAII,UAAUC,IAAI,aAClBL,EAAII,UAAUC,IAAI,oBACLL,EAAIO,YAAYzD,SAASmD,cAAc,SAC/CO,UAAYT,EAAEI,MACnBH,EAAIS,mBAAmB,kBAAmBV,EAAEW,cAC5BV,EAAIW,WAAW,GACvBP,UAAUC,IAAI,UAEtBtG,EAAGC,cAAcsH,WAAWC,aAAavB,EAAKjG,EAAGC,eAE7C6E,EAAW,CACEpF,KAAKsF,SAASpF,IAAIkF,GAC1B2C,OAAO,CAACC,QAAS9C,EAAK2C,WAAWN,aAI3CnH,eAAe8H,EAASC,EAAUC,GACjC,IAAIlD,EAAO7B,SAASmD,cAAc,OAGlC,GAFAtB,EAAKyB,UAAUC,IAAI,WACnB1B,EAAK8B,mBAAmB,aAAcoB,GAClCD,EAASE,IAAK,CACjB,IAAIC,EAAWpD,EAAKoC,cAAc,uBAC9BgB,SACGvD,EAAQ,CAACxE,cAAe+H,IAGhC,GAAIH,EAASI,IAAK,CACjB,MAAMC,EAAOC,MAAMC,KAAKxD,EAAKsC,iBAAiB,qBAC9C,IAAK,MAAMmB,KAAOH,QACXP,EAAQ,CAAC1H,cAAeoI,IAEhC,OAAOzD,EAAKqC,UAGbnH,eAAeM,EAAaJ,GACX,UAAZA,EAAGqB,OACNrB,EAAGsI,iBACHtI,EAAGuI,mBAIJ,MAAM7D,EAAS1E,EAAGC,cAClByE,EAAOC,UAAW,EAClB,MAAMC,EAAOF,EAAOG,QAAQ,cAGtBnE,EAAQ0D,OAAOkB,KAAKC,YAAYC,kBAAkBZ,GAExD,IAAMlE,IAAUA,EAAMuE,MAAO,OAG7B,MAAMtE,EAAOD,EAAM+E,aAAab,EAAK1E,QAAQwF,QAC7C,IAAM/E,EACL,OAAOwE,GAAGC,cAAcC,MAAM,sBAAsBT,EAAK1E,QAAQwF,oCAAoChF,EAAMiF,QAG5G,IAAI6C,EAAU9I,KAAK+I,KAAKD,QAIxB,MAAME,EAAYC,OAAOC,KAAKxE,OAAOC,MAAMwE,iBAAiBlH,SAASmH,YAAYnI,EAAM,0BAClF6H,EAAQO,OAAQL,IACpBF,EAAW,CAAC,CAAC1H,KAAM,CACjB6E,KAAM,UACNqD,IAAK,OAGR,MAAM5F,EAAa1B,SAASkD,EAAK1E,QAAQkD,aAAe,KAIlD6F,EAAa,CAClBvI,QAAOC,OACPuI,YAAazI,EAAa,CAACC,QAAOC,SAClCwI,WAAYhG,EAAY,CAACzC,QAAOC,OAAMyC,eACtCgG,QAASC,OAAOC,MAAMC,GACtBd,KAAM/I,KAAK+I,KAAKc,IAGX1B,EAAWnI,KAAKC,SAASC,IAAI,OAAWF,KAAKG,OAAR,sBAE3C,IAAI2J,EAAW9J,KAAKC,SAASC,IAAI,OAAQ,YACzC,IAAK,MAAM6J,KAAUjB,EAAS,CAC7B,MAAMkB,QAAgB/I,EAAKgJ,2BAA2B,CAACC,QAAQ,EAAOC,UAAU,IAC1EC,EAAqB,IACjBb,EACHQ,OAAQA,EAAO3I,KACfiJ,OAAQpJ,EAAKG,KAAKA,KAAKkJ,WAAWC,QAAQ,oBAAqBR,EAAO3I,KAAK6E,MAC3E+D,WAEP,IAAIQ,QAAa5J,eArBD,0CAqB0BwJ,IAItCjC,EAASE,KAAOF,EAASI,OAC5BiC,QAAatC,EAASC,EAAUqC,IAGjC,IAAIC,EAAW,CACX1B,KAAM/I,KAAK+I,KAAK2B,IAChB/I,KAAMgJ,MAAMC,mBAAmBC,MAC/B7C,QAASwC,EACTM,QAAS,CACP9J,MAAOC,EAAKD,MAAM0J,IAClBK,MAAO9J,EAAKD,MAAM+J,MAClBC,MAAO/J,EAAKD,MAAMiF,OAGnB,CAAC,SAAU,aAAahE,SAAS6H,KAAYW,EAAkB,QAAIQ,YAAYC,cAAc,OAChF,cAAbpB,IAA2BW,EAAgB,OAAI,GAEpDQ,YAAYE,OAAOV,GAGpBzF,EAAOC,UAAW,EAGnB7E,eAAegL,EAAe9K,GAC7B,MAAM4E,EAAO5E,EAAGC,cAAc4E,QAAQ,qBAEtC,GADgBD,EAAK1E,QAAQkJ,UACbC,OAAOC,MAAMC,GAAI,OAAO,EACxC,MAAMwB,EAAUnG,EAAK1E,QAAQ8K,SAC7B,IAAKD,EAAS,OAAO,EAErB,MAAMN,EAAQpB,OAAO4B,OAAOC,WAAWC,KAAK/D,GAAKA,EAAEmC,KAAOwB,GAC1D,OAAKN,IAAc,EAIpB3K,eAAesL,EAAmBpL,GACjCA,EAAGsI,iBACHtI,EAAGuI,kBACH,MAAMkC,QAAcK,EAAe9K,GACnC,IAAKyK,EAAO,OAAO,EAEnBA,EAAMY,aAGPvL,eAAewL,EAAmBtL,GACjCA,EAAGsI,iBACHtI,EAAGuI,kBACH,MAAMkC,QAAcK,EAAe9K,GACnC,IAAKyK,IAAUA,EAAMc,QAAS,OAAO,EAErCd,EAAMe,cAGP1L,eAAe2L,EAAiBzL,GAC/BA,EAAGsI,iBACHtI,EAAGuI,kBACH,MAAMkC,QAAcK,EAAe9K,GACnC,IAAKyK,IAAUA,EAAMc,QAAS,OAAO,EAErC,MAAMG,EAAMjB,EAAMkB,OAClBtC,OAAOuC,WAAW,CAACC,EAAGH,EAAIG,EAAGC,EAAGJ,EAAII,IAKrC,SAASC,EAActB,GACtB,MAAMuB,EAAO3C,OAAOC,MAAMxI,KAAKkL,KAC5BC,EAAkB1L,KAAKO,KAAK+K,EAA5BI,EAAkC1L,KAAKO,KAAKgL,EAGzCI,EAASzB,EAAM0B,OAAS,EAAI,GAAM1B,EAAM0B,MAAQ,EAChDC,EAAS3B,EAAM4B,QAAU,EAAI,GAAM5B,EAAM4B,OAAS,EACxD,IAAK,IAAIR,EAAIK,EAAQL,EAAIpB,EAAM0B,MAAON,IACrC,IAAK,IAAIC,EAAIM,EAAQN,EAAIrB,EAAM4B,OAAQP,IAAK,CAC3C,MAAMQ,EAAW,CAChBT,EAAGpB,EAAMoB,EAAIA,EAAIG,EAAOC,EACxBH,EAAGrB,EAAMqB,EAAIA,EAAIE,EAAOC,GAGzB,GADiB1L,KAAKgM,MAAMC,SAASF,EAAST,EAAGS,EAASR,GAC5C,OAAO,EAGvB,OAAO,EAGR,SAASW,IACR,MAAMxB,EAAS5B,OAAOC,MAAMoD,sBAAsB,SAClD,IAAIlE,EAAU,GAEd,IAAK,MAAMiC,KAASQ,EACf1K,KAAKwL,cAActB,IAAUjC,EAAQjH,KAAKkJ,EAAML,KACrD1K,KAAK+I,KAAKkE,mBAAmBnE,GA4C9B1I,eAAe8M,EAASC,EAAK3C,GAC5B,MAAMjE,EAAMlD,SAASmD,cAAc,OACnCD,EAAII,UAAUC,IAAI,cAClBL,EAAIO,YAAYzD,SAASmD,cAAc,UAAUO,UAAY,mBAC7D,MAAMqG,EAAY7G,EAAIO,YAAYzD,SAASmD,cAAc,QACzD4G,EAAUzG,UAAUC,IAAI,eACxB,MAAMyG,EAAMD,EAAUtG,YAAYzD,SAASmD,cAAc,UACzD6G,EAAI7M,QAAQ8M,MAAQ,SACpBD,EAAI1L,KAAO,OACX0L,EAAIpH,KAAO,6BACXoH,EAAI9J,MAAQ4J,EAAII,OAAOC,QAAQ,OAAQ,oBAAsB,GAE7DJ,EAAUpG,mBAAmB,YAAa,0NAK1C,MAAMhC,EAASoI,EAAU9F,cAAc,UACvCtC,EAAOyI,MAAMC,KAAO,IACpBP,EAAIQ,oBAAoB3I,GACxBwF,EAAK,GAAGlD,cAAc,8BAA8BnC,QAAQ,eAAeyI,MAAMrH,GAGlFnG,eAAeyN,EAAQzM,GAItB,IAAI8E,EAAMnG,IAENoG,EAAK,EACLC,EAAOhF,EAAKgB,cAAgB,MAAQ,GAG3B,cAAR8D,GACJC,EAAK/E,EAAKc,cAAgB,EAAI,EAC9BkE,GAAQ,KACRhF,EAAKqF,OAAS,KAAKzG,KAAK6E,KAAKC,SAAS,uBAIrB,iBAARoB,IACTC,EAAK,EACLC,GAAQ,KACRhF,EAAKqF,OAAS,KAAKzG,KAAK6E,KAAKC,SAAS,0BAIvC,IAAIgJ,EAAc,GAAG3H,OAAQC,IACzBhF,EAAK2M,iBAAgBD,EAAc,IAAI3H,OAAQC,WACnDhF,EAAKM,MAAM2E,QAAQyH,GAEnB,MAAME,EAxiBC3K,SAASC,eAAe,iBAAiBC,MAyiB5CyK,GACH5M,EAAKM,MAAMG,KAAKmM,GAEjB,IAAI1H,EAAI,IAAIxD,KAAK1B,EAAKM,MAAMqB,KAAK,KAAM3B,GACvCkF,EAAEzD,OACF,MAAMwE,EAAMf,EAAE5E,MAAM,GAAGgF,MACvB,IAAIuH,EAAe,IAAI7M,EACtB8M,cAAe5H,EAAEW,aACjBpE,KAAMyD,EACNa,KAAOE,GAAO,GACdD,OAAQC,GAAO,GAGhB,MAAMe,QAAiBxH,eAAe,wCAAyCqN,GAE/E,IAAIxD,EAAW,CACd1B,KAAM/I,KAAK+I,KAAK2B,IAChB/I,KAAMgJ,MAAMC,mBAAmBC,MAC/B7C,QAASI,EACT0C,QAAS,CACR9J,MAAOH,KACPmK,MAAOnK,KAAKoF,OAGV6D,EAAW9J,KAAKC,SAASC,IAAI,OAAQ,YACpC,CAAC,SAAU,aAAa+B,SAAS6H,KAAYW,EAAkB,QAAIQ,YAAYC,cAAc,OAChF,cAAbpB,IAA2BW,EAAgB,OAAI,GAEpDQ,YAAYE,OAAOV,GAGpBrK,eAAe+N,EAAehB,EAAK3C,EAAMpJ,GAGxC,MAAMgN,EAAc5D,EAAK,GAAGhD,iBAAiB,+BAC7C6G,EAAED,GAAaE,MACfF,EAAY3G,QAAQC,GAAKA,EAAE6G,iBAAiB,SAAS,SAASjO,GAC7DA,EAAGuI,kBACHvI,EAAGsI,iBAEH,MAAM4F,EAAYlO,EAAGC,cAAc4E,QAAQ,YAAY3E,QAAQiO,QACzDC,EAAQhK,OAAOC,MAAMgK,UAAUH,GAE7B9M,EAAQ,CAAC,QACTN,EAAO,CAACwN,IAFFzB,EAAII,OAAOnM,KAAKA,KAAKuN,UAAUH,GAEpBI,KACjBC,EAAQ1B,EAAII,OAAOnM,KAAKE,MAAMC,OAAS,GAGxCsN,EAAMC,mBAAqBnK,MAAMoK,eAAeD,kBAAkBH,UAAU1M,SAASuM,IACxF9M,EAAMG,KAAK,gBACXT,EAAK4N,YAAcC,KAAKC,KAAK,GAAMrO,KAAKO,KAAKA,KAAK+N,WAAWC,OAErDP,EAAMQ,kBACd3N,EAAMG,KAAK,gBACXT,EAAK4N,YAAcC,KAAKK,MAAM,GAAMzO,KAAKO,KAAKA,KAAK+N,WAAWC,OAIhE,IAAI/M,EAAa+G,YAAY+D,EAAII,OAAOnM,KAAKA,KAAKkB,QAAS,mBAW7D,OAVSD,IACLX,EAAMG,KAAK,eACXT,EAAKmO,WAAalN,GAGtBjB,EAAKM,MAAQA,EAEbN,EAAKqF,MAAQzG,KAAK6E,KAAK2K,OAAO,2BAA4B,CAACf,QAASC,IAEpEb,EAAQ4B,KAAKtC,EAAII,OAAjBM,CAAyBzM,IAClB,MAESoJ,EAAK,GAAGhD,iBAAiB,iBACjCC,QAAQC,GAAKA,EAAE6G,iBAAiB,SAAS,SAASjO,GAC1DA,EAAGuI,kBACHvI,EAAGsI,iBACH,MAAM4F,EAAYlO,EAAGC,cAAc4E,QAAQ,YAAY3E,QAAQiO,QACzDC,EAAQhK,OAAOC,MAAMgK,UAAUH,GAC7BkB,EAAMvC,EAAII,OAAOnM,KAAKA,KAAKuN,UAAUH,GACrC9M,EAAQ,CAAC,QACTN,EAAO,CAACwN,IAAKc,EAAId,KAGlBc,EAAIN,KAAO,IACd1N,EAAMG,KAAK,SACXT,EAAKgO,KAAOM,EAAIN,MAIlB,MAAM/M,EAAa+G,YAAY+D,EAAII,OAAOnM,KAAKA,KAAKkB,QAAS,kBACtDD,IACLX,EAAMG,KAAK,cACXT,EAAKuO,UAAYtN,GAErBjB,EAAKqF,MAAQzG,KAAK6E,KAAK2K,OAAO,wBAAyB,CAACf,QAASC,IACjEtN,EAAKM,MAAQA,EACbmM,EAAQ4B,KAAKtC,EAAII,OAAjBM,CAAyBzM,OAG1B,MAAMwO,EAASpF,EAAK,GAAGhD,iBAAiB,eACxC6G,EAAEuB,GAAQtB,MACVsB,EAAOnI,QAAQC,GAAKA,EAAE6G,iBAAiB,SAAS,SAASjO,GACxDA,EAAGuI,kBACHvI,EAAGsI,iBACH,MAAMiH,EAAUvP,EAAGC,cAAc4E,QAAQ,UAAU3E,QAAQsP,MACrDC,EAAM5C,EAAII,OAAOnM,KAAKA,KAAKwO,OAAOC,GAGhCnO,EAAQ,CAAC,QACTN,EAAO,CAACwN,IAAKmB,EAAInB,IAAMmB,EAAIX,MAC5BW,EAAIC,QACP5O,EAAiB,WAAI2O,EAAIC,MACzBtO,EAAMG,KAAK,gBAIWkO,EAAIxM,OAAS,GAAK4J,EAAII,OAAOC,QAAQ,QAAS,kBAKxE,OAJApM,EAAKM,MAASA,EACdN,EAAKqF,MAAQzG,KAAK6E,KAAK2K,OAAO,yBAA0B,CAACM,MAAOpL,OAAOC,MAAMiL,OAAOC,KAEpFhC,EAAQ4B,KAAKtC,EAAII,OAAjBM,CAAyBzM,IAClB,MAKMhB,eAAe6P,IAC7BjQ,KAAKC,SAASiQ,SAAS,OAAWlQ,KAAKG,OAAR,gBAA+B,CAC7D8F,KAAM,4BACNkK,QAAS,SACTxO,KAAMyO,OACNC,MAAO,SAERrQ,KAAKC,SAASiQ,SAAS,OAAWlQ,KAAKG,OAAR,qBAAoC,CAClE8F,KAAM,2BACNkK,QAAS,CAAC9H,KAAK,EAAOE,KAAK,GAC3B5G,KAAMsH,OACNoH,MAAO,SAKRC,MAAMC,GAAG,gBAAiBnQ,MAAO+M,EAAK3C,EAAMpJ,KAC3C,MAAMmF,QAprBRnG,iBACC,MAAMmG,EAAMlD,SAASmD,cAAc,OACnCD,EAAII,UAAUC,IAAI,qBAElB,MAAM4J,EAAcxQ,KAAKC,SAASC,IAAI,OAAWF,KAAKG,OAAR,iBAExC8N,EAAe,CACpBwC,UAA2B,cAAhBD,EACXE,OAAyB,WAAjBF,EACRG,aAA+B,iBAAjBH,KAJUxQ,KAAKC,SAASC,IAAI,OAAWF,KAAKG,OAAR,uBAyCnD,OAjCAoG,EAAIS,mBAAmB,mBAAoBpG,eAAe,2CAA4CqN,IAEtG1H,EAAIiB,iBAAiB,wBAAwBC,QAAQC,IACpDA,EAAE6G,iBAAiB,SAASnO,eAAeE,GAC1CA,EAAGsI,iBACHtI,EAAGuI,kBAQH,MAAM+H,EAAMnI,MAAMC,KAAKpI,EAAGC,cAAcsH,WAAWL,iBAAiB,MAC9DqJ,EAAUD,EAAIE,UAAUpJ,GAAKA,EAAEf,UAAUmG,SAAS,kBACxD8D,EAAIC,GAASlK,UAAUoK,OAAO,iBAC9B,MAAMC,EAAcJ,GAAKC,EAAU,GAAKD,EAAIpM,QAC5CwM,EAAYrK,UAAUC,IAAI,iBAC1B5G,KAAKC,SAASgR,IAAI,OAAWjR,KAAKG,OAAR,gBAA+B6Q,EAAY/K,WAGvEM,EAAIiB,iBAAiB,6BAA8BC,QAAQC,IAC1DA,EAAE6G,iBAAiB,SAASnO,eAAeE,GAC1CA,EAAGsI,iBACHtI,EAAGuI,kBAGHvI,EAAGC,cAAcoG,UAAUuK,OAAO,iBAClC,IAAI9P,EAAOpB,KAAKC,SAASC,IAAI,OAAWF,KAAKG,OAAR,sBACrCiB,EAAKd,EAAGC,cAAc0F,MAAQ3F,EAAGC,cAAcoG,UAAUmG,SAAS,iBAClE9M,KAAKC,SAASgR,IAAI,OAAWjR,KAAKG,OAAR,qBAAoCiB,QAGzDmF,EAsoBY4K,GACZC,EAAW5G,EAAK,GAAGlD,cAAc,kBACvC8J,EAASC,aAAa9K,EAAK6K,EAASlK,WAAW,MAKhDxC,OAAOkB,KAAKC,YAAYyL,cAAgB,SAAU9G,GAC/CA,EAAK+F,GAAG,QAAS,uBAAwBlQ,EAAiBoP,KAAK5O,OACjE2J,EAAK+F,GAAG,QAAS,aAAc1P,KAAK0Q,yBAAyB9B,KAAK5O,OAGlE2J,EAAK+F,GAAG,aAAc,oBAAqB7E,GAC3ClB,EAAK+F,GAAG,aAAc,oBAAqB3E,GAC3CpB,EAAK+F,GAAG,WAAY,oBAAqBxE,GAEzCvB,EAAK+F,GAAG,QAAS,sBAAuBxL,GACxCyF,EAAK+F,GAAG,QAAS,mBAAoBtI,IAGtCqI,MAAMC,GAAG,uBAAwBnQ,MAAOgB,IACvC,MAAMmF,EAAMlD,SAASmD,cAAc,OACnCD,EAAIS,mBAAmB,aAAe5F,EAAK4G,SAC3C,IAAIW,EAAMpC,EAAIe,cAAc,gCACvBqB,IACJA,EAAMpC,EAAIe,cAAc,iCAErBqB,GACHjI,EAAa,CAACH,cAAeoI,MAI/B2H,MAAMC,GAAG,kBAAmBrD,GAE5BoD,MAAMC,GAAG,mBAAoBpC,GA7Q9B/N,iBACC,MAAMoR,QAAoBC,OAAiC,kDACrDC,EAAkBF,EAAWrB,SAAWqB,EAAWE,gBAGnDC,EAAoBD,EAAgBE,SAC1CF,EAAgBE,SAAW,SAAS3Q,GACnC,MAAMmH,EAAWuJ,EAAkBlC,KAAK5O,KAAvB8Q,CAA6B1Q,GAI9C,IAAI4Q,EAAO5Q,EAAKuM,QAAQ,OAAQ,mBAChC,IAAKqE,GAAQ5Q,EAAK0C,UAAW,CAC5B,MAAM1D,EAAWD,KAAKC,SAASC,IAAI,OAAQ,oBAAsB,GACjE2R,EAAO5R,EAASgB,EAAKG,KAAKA,KAAK0C,OAAOpC,MAAM,GAAG,KAAO,GACtDmQ,EAAOA,EAAKzJ,EAAShH,KAAK0Q,GAS3B,OAPID,GACHE,YAAYF,GAAMG,KAAKC,IACtB7J,EAAS8J,QAAUD,EACnB7J,EAAShH,KAAK8Q,QAAUL,EACxBzJ,EAAS+J,YAEX/J,EAASnH,KAAOA,EACTmH,GAIR,MACMgK,EADoBV,EAAgBW,UAAUC,yBAAyBC,WAC5ChI,QAAQ,sBAErC,+DAIJmH,EAAgBW,UAAUtF,WAAaA,EACvC2E,EAAgBW,UAAUhG,cAAgBA,EAE1CqF,EAAgBW,UAAUC,yBAA2BE,SAAS,mCAAmCJ,MAA5CI,GAyOrDC,GAhuBD","file":"bundles/modify-rolling.4bca.js","sourcesContent":["function getAdvantageSettings() {\n\treturn game.settings.get('mess', `${game.userId}.adv-selector`);\n}\n\nfunction getD20Modifier() {\n\treturn document.getElementById('mess-roll-mod').value;\n}\n\nasync function createControls() {\n\tconst div = document.createElement('div');\n\tdiv.classList.add('mess-roll-control');\n\n\tconst advSelector = game.settings.get('mess', `${game.userId}.adv-selector`);\n\tconst autoRollSelector = game.settings.get('mess', `${game.userId}.autoroll-selector`);\n\tconst templateData = {\n\t\tadvantage: advSelector === 'advantage',\n\t\tnormal: advSelector === 'normal',\n\t\tdisadvantage: advSelector === 'disadvantage',\n\t\t...autoRollSelector\n\t}\n\n\tdiv.insertAdjacentHTML('afterbegin', await renderTemplate('modules/mess/templates/roll-control.html', templateData));\n\n\tdiv.querySelectorAll('.mess-adv-selector a').forEach(e => {\n\t\te.addEventListener('click', async function(ev) {\n\t\t\tev.preventDefault();\n\t\t\tev.stopPropagation();\n\n\t\t\t// if (ev.currentTarget.classList.contains('mess-selected')) return false;\n\n\t\t\t// ev.currentTarget.parentNode.querySelector('.mess-selected').classList.remove('mess-selected');\n\t\t\t// ev.currentTarget.classList.add('mess-selected');\n\n\t\t\t// game.settings.set('mess', `${game.userId}.adv-selector`, ev.currentTarget.name);\n\t\t\tconst arr = Array.from(ev.currentTarget.parentNode.querySelectorAll('a'));\n\t\t\tconst currIdx = arr.findIndex(e => e.classList.contains('mess-selected'));\n\t\t\tarr[currIdx].classList.remove('mess-selected');\n\t\t\tconst newSelected = arr[(currIdx + 1) % arr.length];\n\t\t\tnewSelected.classList.add('mess-selected');\n\t\t\tgame.settings.set('mess', `${game.userId}.adv-selector`, newSelected.name);\n\t\t});\n\t});\n\tdiv.querySelectorAll('.mess-autoroll-selector a') .forEach(e => {\n\t\te.addEventListener('click', async function(ev) {\n\t\t\tev.preventDefault();\n\t\t\tev.stopPropagation();\n\n\t\t\t\n\t\t\tev.currentTarget.classList.toggle('mess-selected');\n\t\t\tlet data = game.settings.get('mess', `${game.userId}.autoroll-selector`);\n\t\t\tdata[ev.currentTarget.name] = ev.currentTarget.classList.contains('mess-selected');\n\t\t\tgame.settings.set('mess', `${game.userId}.autoroll-selector`, data);\n\t\t})\n\t});\n\treturn div;\n}\n\n// Only overwrite stuff for attack buttons\nasync function onChatCardAction (ev) {\n\tif (ev.currentTarget.dataset.action === 'attack')\n\t\treturn renderAttack(ev);\n\tif (ev.currentTarget.dataset.action === 'damage')\n\t\treturn renderAttack(ev);\n\tif (ev.currentTarget.dataset.placeTemplate)\n\t\treturn renderTemplate(ev);\n\n\treturn this._onChatCardAction(ev);\t\t\n}\n\nasync function getToHitData({actor, item}) {\n\tif (!item.hasAttack) return null;\n\tconst actorData = actor.data.data;\n\tconst itemData = item.data.data;\n\tconst flags = actor.data.flags.dnd5e || {};\n\t\n\tlet rollData = item.getRollData();\n\n\t// Define Roll bonuses\n\tconst parts = [`@mod`];\n\tif ( (item.data.type !== \"weapon\") || itemData.proficient ) {\n\t\tparts.push(\"@prof\");\n\t}\n\trollData.parts = parts;\n\n\t// Expanded weapon critical threshold\n\tif (( item.data.type === \"weapon\" ) && flags.weaponCriticalThreshold) {\n\t\trollData.critical = parseInt(flags.weaponCriticalThreshold);\n\t}\n\n\t// Elven Accuracy\n\tif ( [\"weapon\", \"spell\"].includes(item.data.type) ) {\n\t\tif (flags.elvenAccuracy && [\"dex\", \"int\", \"wis\", \"cha\"].includes(item.abilityMod)) {\n\t\t\trollData.elvenAccuracy = true;\n\t\t}\n\t}\n\n\t// Apply Halfling Lucky\n\tif ( flags.halflingLucky ) rollData.halflingLucky = true;\n\n\t// Attack Bonus\n\tconst actorBonus = actorData.bonuses[itemData.actionType] || {};\n\tif ( itemData.attackBonus || actorBonus.attack ) {\n\t\t// parts.push(\"@atk\");\n\t\trollData[\"atk\"] = [itemData.attackBonus, actorBonus.attack].filterJoin(\" + \");\n\t\tif (!isNaN(Number(rollData[\"atk\"]))) {\n\t\t\tparts.push(\"@atk\");\n\t\t}\n\t}\n\n\tlet roll = new Roll(rollData.parts.join('+'), rollData);\n\trollData.totalModifier = roll._safeEval(roll.formula);\n\trollData.totalModifier = rollData.totalModifier >= 0 ? '+' + rollData.totalModifier : rollData.totalModifier;\n\tif (rollData[\"atk\"] && !roll._formula.includes('@atk')) {\n\t\trollData.parts.push(\"@atk\");\n\t\troll = new Roll(rollData.parts.join('+'), rollData);\n\t\trollData.totalModifier += `+${rollData[\"atk\"]}`;\n\t}\n\tconst situationalModifier = document.getElementById('mess-roll-mod');\n\tif (situationalModifier.value) {\n\t\trollData.parts.push(situationalModifier.value);\n\t\troll = new Roll(rollData.parts.join('+'), rollData);\n\t\trollData.totalModifier += `+${situationalModifier.value}`;\n\t}\n\trollData.formula = roll.formula;\n\trollData.terms = roll._formula;\n\treturn rollData;\n}\n\nasync function getDmgsData({actor, item, spellLevel = null}) {\n\tif (!item.hasDamage) return null;\n\tconst actorData = actor.data.data;\n\tconst itemData = item.data.data;\n\tlet rollData = item.getRollData();\n\t\n\tif ( spellLevel ) rollData.item.level = spellLevel;\n\n\trollData.parts = duplicate(itemData.damage.parts);\n\tif (itemData.damage.versatile) \n\t\trollData.parts.splice(1, 0, [itemData.damage.versatile, \"versatile\"]);\n\t\n\tif (item.data.type === 'spell') {\n\t\tif (itemData.scaling.mode === 'cantrip') {\n\t\t\tlet newDmgPart = [rollData.parts[0][0]];\n\t\t\tconst lvl = actor.data.type === 'character' ? actorData.details.level : actorData.details.spellLevel;\n\t\t\titem._scaleCantripDamage(newDmgPart, lvl, itemData.scaling.formula);\n\t\t\trollData.parts[0][0] = newDmgPart[0];\n\t\t} else if (spellLevel && (itemData.scaling.mode === 'level') && itemData.scaling.formula ) {\n\t\t\tlet newDmgPart = [];\n\t\t\titem._scaleSpellDamage(newDmgPart, itemData.level, spellLevel, itemData.scaling.formula)\n\t\t\tif (newDmgPart.length > 0) {\n\t\t\t\tnewDmgPart.push('upcast dice');\n\t\t\t\trollData.parts.push(newDmgPart);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tconst actorBonus = actorData.bonuses[itemData.actionType] || {};\n\tif (actorBonus.damage && parseInt(actorBonus.damage ) !== 0) {\n\t\tparts[0][0] += \"+@dmg\";\n\t\trollData[\"dmg\"] = actorBonus.damage;\n\t}\n\n\tfor (let part of rollData.parts) {\n\t\tlet roll = new Roll(part[0], rollData);\n\t\tconst dmgType = CONFIG.DND5E.damageTypes[part[1]];\n\t\tif (dmgType)\n\t\t\tpart[1] = game.i18n.localize('DND5E.Damage' + CONFIG.DND5E.damageTypes[part[1]]);\n\t\telse if (part[1] === 'versatile')\n\t\t\tpart[1] = game.i18n.localize('DND5E.Versatile');\n\t\tpart.push(roll.formula);\n\t}\n\n\treturn rollData;\n}\n\nasync function rollHit(ev) {\n\t// Extract card data\n\tconst button = ev.currentTarget;\n\tbutton.disabled = true;\n\tconst card = button.closest(\".chat-card\");\n\tconst messageId = card.closest(\".message\").dataset.messageId;\n\t// Check if user owns chat message, else return\n\tif (messageId) {\n\t\tconst message = game.messages.get(messageId);\n\t\tif (!(message.owner || message.isAuthor)) {\n\t\t\tui.notifications.error('You do not own the permissions to make that roll!');\n\t\t\treturn;\n\t\t}\n\t}\n\t// Get the Actor from a synthetic Token\n\tconst actor = CONFIG.Item.entityClass._getChatCardActor(card);\n\tif (!actor.owner) return false;\n\n\t// Get the Item\n\tconst item = actor.getOwnedItem(card.dataset.itemId);\n\tif ( !item ) {\n\t\treturn ui.notifications.error(`The requested item ${card.dataset.itemId} no longer exists on Actor ${actor.name}`)\n\t}\n\n\tlet rollData = await getToHitData({actor, item});\n\tlet adv = getAdvantageSettings();\n\t// Determine the d20 roll and modifiers\n\tlet nd = 1;\n\tlet mods = rollData.halflingLucky ? \"r=1\" : \"\";\n\n\t// Handle advantage\n\tif ( adv === \"advantage\" ) {\n\t\tnd = rollData.elvenAccuracy ? 3 : 2;\n\t\tmods += \"kh\";\n\t}\n\n\t// Handle disadvantage\n\telse if ( adv === \"disadvantage\" ) {\n\t\tnd = 2;\n\t\tmods += \"kl\";\n\t}\n\n\t// Include the d20 roll\n\trollData.parts.unshift(`${nd}d20${mods}`);\n\t\n\tlet r = new Roll(rollData.parts.join('+'), rollData);\n\tr.roll();\n\tlet div = document.createElement('div');\n\tdiv.title = `${rollData.parts[0]}+${rollData.terms} = ${r.formula} = ${r.total}. Click to see rolls.`;\n\tdiv.classList.add('dice-roll');\n\tdiv.classList.add('mess-dice-result');\n\tconst span = div.appendChild(document.createElement('span'));\n\tspan.innerText = r.total;\n\tdiv.insertAdjacentHTML('beforeend', await r.getTooltip());\n\tconst tooltip = div.childNodes[1];\n\ttooltip.classList.add('hidden');\n\tconst crit = rollData.critical || 20;\n\tconst fumble = rollData.fumble || 1;\n\n\tconst d20 = r.parts[0].total;\n\tif (d20 >= crit) {\n\t\tspan.classList.add('crit');\n\t\tcard.querySelector('.mess-chat-dmg .mess-chat-roll-type').innerHTML += ' - Crit!'\n\t\tcard.querySelectorAll('.mess-button-dmg').forEach((e, idx) => {\n\t\t\tconst formula = e.dataset.formula;\n\t\t\tconst r = new Roll(formula);\n\t\t\tr.alter(0, 2);\n\t\t\te.innerHTML = ` ${r.formula}`\n\t\t\te.dataset.formula = r.formula;\n\t\t});\n\t}\n\tif (d20 <= fumble)\n\t\tspan.classList.add('fumble');\n\n\tev.currentTarget.parentNode.replaceChild(div, ev.currentTarget);\n\tif (messageId) {\n\t\tconst message = game.messages.get(messageId);\n\t\tmessage.update({content: card.parentNode.innerHTML});\n\t}\n}\n\nasync function rollDmg(ev) {\n\t// Extract card data\n\tconst button = ev.currentTarget;\n\tbutton.disabled = true;\n\tconst card = button.closest(\".chat-card\");\n\tconst messageId = card.closest(\".message\").dataset.messageId;\n\n\t// Check if user owns chat message, else return\n\tif (messageId) {\n\t\tconst message = game.messages.get(messageId);\n\t\tif (!(message.owner || message.isAuthor)) {\n\t\t\tui.notifications.error('You do not own the permissions to make that roll!');\n\t\t\treturn;\n\t\t}\n\t}\n\tconst formula = button.dataset.formula;\n\n\tlet r = new Roll(formula);\n\tr.roll();\n\tlet div = document.createElement('div');\n\tdiv.title = `${button.dataset.terms} = ${r.formula} = ${r.total}. Click to see rolls.`;\n\tdiv.classList.add('dice-roll');\n\tdiv.classList.add('mess-dice-result');\n\tconst span = div.appendChild(document.createElement('span'));\n\tspan.innerText = r.total;\n\tdiv.insertAdjacentHTML('beforeend', await r.getTooltip());\n\tconst tooltip = div.childNodes[1];\n\ttooltip.classList.add('hidden');\n\n\tev.currentTarget.parentNode.replaceChild(div, ev.currentTarget);\n\n\tif (messageId) {\n\t\tconst message = game.messages.get(messageId);\n\t\tmessage.update({content: card.parentNode.innerHTML});\n\t}\n}\n\nasync function autoRoll(autoroll, template) {\n\tlet card = document.createElement('div');\n\tcard.classList.add('message');\n\tcard.insertAdjacentHTML('afterbegin', template);\n\tif (autoroll.hit) {\n\t\tlet toHitBtn = card.querySelector('.mess-button-to-hit');\n\t\tif (toHitBtn)\n\t\t\tawait rollHit({currentTarget: toHitBtn});\n\t}\n\n\tif (autoroll.dmg) {\n\t\tconst btns = Array.from(card.querySelectorAll('.mess-button-dmg'));\n\t\tfor (const btn of btns)\n\t\t\tawait rollDmg({currentTarget: btn});\n\t}\n\treturn card.innerHTML;\n}\n\nasync function renderAttack(ev) {\n\tif (ev.type === 'click') {\n\t\tev.preventDefault();\n\t\tev.stopPropagation();\n\t}\n\n\t// Extract card data\n\tconst button = ev.currentTarget;\n\tbutton.disabled = true;\n\tconst card = button.closest(\".chat-card\");\n\n\t// Get the Actor from a synthetic Token\n\tconst actor = CONFIG.Item.entityClass._getChatCardActor(card);\n\n\tif ( !actor || !actor.owner) return;\n\n\t// Get the Item\n\tconst item = actor.getOwnedItem(card.dataset.itemId);\n\tif ( !item ) {\n\t\treturn ui.notifications.error(`The requested item ${card.dataset.itemId} no longer exists on Actor ${actor.name}`)\n\t}\n\n\tlet targets = game.user.targets;\n\t// Don't roll for all targets if its an AoE, due to only rolling e.g. dmg once for all targets\n\t// TODO: Maybe add target list or chat cards for making saving throws\n\t// or not, since it would just spam the chatlog.. hmm\n\tconst areaSkill = Object.keys(CONFIG.DND5E.areaTargetTypes).includes(getProperty(item, 'data.data.target.type'));\n\tif (!targets.size || areaSkill)\n\t\ttargets = [{data: {\n\t\t\t\tname: \"someone\",\n\t\t\t\timg: \"\"\n\t\t\t}\n\t\t}];\n\tconst spellLevel = parseInt(card.dataset.spellLevel) || null;\n\n\tconst template = 'modules/mess/templates/attack-card.html';\n\n\tconst attackData = {\n\t\tactor, item,\n\t\ttoHit: await getToHitData({actor, item}),\n\t\tdmgs: await getDmgsData({actor, item, spellLevel}),\n\t\tsceneId: canvas.scene.id,\n\t\tuser: game.user.id\n\t}\n\n\tconst autoroll = game.settings.get('mess', `${game.userId}.autoroll-selector`);\n\n\tlet rollMode = game.settings.get(\"core\", \"rollMode\");\n\tfor (const target of targets) {\n\t\tconst allowed = await item._handleResourceConsumption({isCard: false, isAttack: true});\n\t\tconst attackTemplateData = {\n\t\t\t\t\t\t\t\t\t...attackData, \n\t\t\t\t\t\t\t\t\ttarget: target.data,\n\t\t\t\t\t\t\t\t\tflavor: item.data.data.chatFlavor.replace(/\\[target\\.name\\]/g, target.data.name),\n\t\t\t\t\t\t\t\t\tallowed\n\t\t\t\t\t\t\t\t};\n\t\tlet html = await renderTemplate(template, attackTemplateData);\n\n\t\t\n\n\t\tif (autoroll.hit || autoroll.dmg) \n\t\t\thtml = await autoRoll(autoroll, html);\n\n\n\t\tlet chatData = {\n user: game.user._id,\n type: CONST.CHAT_MESSAGE_TYPES.OTHER,\n content: html,\n speaker: {\n actor: item.actor._id,\n token: item.actor.token,\n alias: item.actor.name\n\t\t\t}\n\t\t};\n\t\tif ( [\"gmroll\", \"blindroll\"].includes(rollMode) ) chatData[\"whisper\"] = ChatMessage.getWhisperIDs(\"GM\");\n\t\tif ( rollMode === \"blindroll\" ) chatData[\"blind\"] = true;\n\t\n\t\tChatMessage.create(chatData);\n\t}\n\n\tbutton.disabled = false;\n}\n\nasync function getTargetToken(ev) {\n\tconst card = ev.currentTarget.closest('.mess-attack-card');\n\tconst sceneId = card.dataset.sceneId;\n\tif (sceneId !== canvas.scene.id) return false;\n\tconst tokenId = card.dataset.targetId;\n\tif (!tokenId) return false;\n\n\tconst token = canvas.tokens.placeables.find(e => e.id === tokenId);\n\tif (!token) return false;\n\treturn token;\n}\n\nasync function onMouseEnterTarget(ev) {\n\tev.preventDefault();\n\tev.stopPropagation();\n\tconst token = await getTargetToken(ev);\n\tif (!token) return false;\n\n\ttoken._onHoverIn();\n}\n\nasync function onMouseLeaveTarget(ev) {\n\tev.preventDefault();\n\tev.stopPropagation();\n\tconst token = await getTargetToken(ev);\n\tif (!token || !token.visible) return false;\n\t\n\ttoken._onHoverOut();\n}\n\nasync function onDblClickTarget(ev) {\n\tev.preventDefault();\n\tev.stopPropagation();\n\tconst token = await getTargetToken(ev);\n\tif (!token || !token.visible) return false;\n\t\n\tconst pos = token.center;\n\tcanvas.animatePan({x: pos.x, y: pos.y})\n}\n\n\n\nfunction isTokenInside(token) {\n\tconst grid = canvas.scene.data.grid,\n\t\t\t\ttemplatePos = {x: this.data.x, y: this.data.y};\n\t// Check for center of each square the token uses.\n\t// e.g. for large tokens all 4 squares\n\tconst startX = token.width >= 1 ? 0.5 : token.width / 2;\n\tconst startY = token.height >= 1 ? 0.5 : token.height / 2;\n\tfor (let x = startX; x < token.width; x++) {\n\t\tfor (let y = startY; y < token.height; y++) {\n\t\t\tconst currGrid = {\n\t\t\t\tx: token.x + x * grid - templatePos.x,\n\t\t\t\ty: token.y + y * grid - templatePos.y\n\t\t\t};\n\t\t\tconst contains = this.shape.contains(currGrid.x, currGrid.y);\n\t\t\tif (contains) return true;\n\t\t}\n\t}\n\treturn false;\n}\n\nfunction getTargets() {\n\tconst tokens = canvas.scene.getEmbeddedCollection('Token');\n\tlet targets = [];\n\t\n\tfor (const token of tokens)\n\t\tif (this.isTokenInside(token)) { targets.push(token._id); }\n\tgame.user.updateTokenTargets(targets);\n}\n\nasync function changeAbilityTemplate() {\n\tconst importedJS = (await import(/* webpackIgnore: true */ '/systems/dnd5e/module/pixi/ability-template.js'))\n\tconst AbilityTemplate = importedJS.default || importedJS.AbilityTemplate;\n\n\t\n\tconst _originalFromItem = AbilityTemplate.fromItem;\n\tAbilityTemplate.fromItem = function(item) {\n\t\tconst template = _originalFromItem.bind(this)(item);\n\t\t\n\t\t// generate a texture based on the items dmg type, ...\n\t\t// Add settings to define custom templates for stuff.\n\t\tlet path = item.getFlag('mess', 'templateTexture');\n\t\tif (!path && item.hasDamage) {\n\t\t\tconst settings = game.settings.get('mess', 'templateTexture') || {};\n\t\t\tpath = settings[item.data.data.damage.parts[0][1]] || {};\n\t\t\tpath = path[template.data.t];\n\t\t}\n\t\tif (path)\n\t\t\tloadTexture(path).then(tex => {\n\t\t\t\ttemplate.texture = tex;\n\t\t\t\ttemplate.data.texture = path;\n\t\t\t\ttemplate.refresh();\n\t\t\t})\n\t\ttemplate.item = item;\n\t\treturn template;\n\t}\n\n\t// rather ugly, maybe find a better way at some point :shrug:\n\tconst origPrevListeners = AbilityTemplate.prototype.activatePreviewListeners.toString();\n\tconst newFun = origPrevListeners.replace(/this\\.refresh\\(\\)\\;/, \n\t\t\t\t// get targets\n\t\t\t\t\t`this.refresh();\n\t\t\t\t\tthis.getTargets(this);\n\t\t\t\t`);\n\n\tAbilityTemplate.prototype.getTargets = getTargets;\n\tAbilityTemplate.prototype.isTokenInside = isTokenInside;\n\n\tAbilityTemplate.prototype.activatePreviewListeners = Function(`\"use strict\"; return ( function ${newFun} )`)();\n}\n\nasync function itemHook(app, html) {\n\tconst div = document.createElement('div');\n\tdiv.classList.add('form-group');\n\tdiv.appendChild(document.createElement('label')).innerText = 'Template Texture';\n\tconst formField = div.appendChild(document.createElement('div'));\n\tformField.classList.add('form-fields');\n\tconst inp = formField.appendChild(document.createElement('input'));\n\tinp.dataset.dtype = 'String';\n\tinp.type = 'text';\n\tinp.name = 'flags.mess.templateTexture';\n\tinp.value = app.object.getFlag('mess', 'templateTexture') || \"\";\n\n\tformField.insertAdjacentHTML('beforeend', `\n\t\t\n\t`);\n\tconst button = formField.querySelector('button');\n\tbutton.style.flex = '0';\n\tapp._activateFilePicker(button);\n\thtml[0].querySelector('[name=\"data.target.units\"]').closest('.form-group').after(div);\n}\n\nasync function rollD20(data) {\n\t// Get the Actor from a synthetic Token\n\t// const actor = this;\n\n\tlet adv = getAdvantageSettings();\n\t// Determine the d20 roll and modifiers\n\tlet nd = 1;\n\tlet mods = data.halflingLucky ? \"r=1\" : \"\";\n\n\t// Handle advantage\n\tif ( adv === \"advantage\" ) {\n\t\tnd = data.elvenAccuracy ? 3 : 2;\n\t\tmods += \"kh\";\n\t\tdata.title += ` (${game.i18n.localize(\"DND5E.Advantage\")})`;\n\t}\n\n\t// Handle disadvantage\n\telse if ( adv === \"disadvantage\" ) {\n\t\tnd = 2;\n\t\tmods += \"kl\";\n\t\tdata.title += ` (${game.i18n.localize(\"DND5E.Disadvantage\")})`;\n\t}\n\n\t// Include the d20 roll\n\tlet diceFormula = `${nd}d20${mods}`;\n\tif (data.reliableTalent) diceFormula = `{${nd}d20${mods},10}kh`;\n\tdata.parts.unshift(diceFormula);\n\n\tconst d20Mod = getD20Modifier();\n\tif (d20Mod)\n\t\tdata.parts.push(d20Mod);\n\t\n\tlet r = new Roll(data.parts.join('+'), data);\n\tr.roll();\n\tconst d20 = r.parts[0].total;\n\tlet templateData = {...data, \n\t\ttooltip: await r.getTooltip(),\n\t\troll: r,\n\t\tcrit: d20 >= 20,\n\t\tfumble: d20 <= 1\n\t}\n\n\tconst template = await renderTemplate('modules/mess/templates/roll-card.html', templateData);\n\n\tlet chatData = {\n\t\tuser: game.user._id,\n\t\ttype: CONST.CHAT_MESSAGE_TYPES.OTHER,\n\t\tcontent: template,\n\t\tspeaker: {\n\t\t\tactor: this,\n\t\t\talias: this.name\n\t\t}\n\t};\n\tlet rollMode = game.settings.get(\"core\", \"rollMode\");\n\tif ( [\"gmroll\", \"blindroll\"].includes(rollMode) ) chatData[\"whisper\"] = ChatMessage.getWhisperIDs(\"GM\");\n\tif ( rollMode === \"blindroll\" ) chatData[\"blind\"] = true;\n\n\tChatMessage.create(chatData);\n}\n\nasync function actorSheetHook(app, html, data) {\n\t// TODO: Redo this with proper methods... this currently ignores the cool new modifier field\n\t// maybe just ignore replace the abilitysave etc functions\n\tconst abilityMods = html[0].querySelectorAll('.ability-mod, .ability-name');\n\t$(abilityMods).off(); // find smth better here!\n\tabilityMods.forEach(e => e.addEventListener('click', function(ev) {\n\t\tev.stopPropagation();\n\t\tev.preventDefault();\n\n\t\tconst abilityId = ev.currentTarget.closest('.ability').dataset.ability;\n\t\tconst label = CONFIG.DND5E.abilities[abilityId];\n const abl = app.object.data.data.abilities[abilityId];\n const parts = [\"@mod\"];\n const data = {mod: abl.mod};\n const feats = app.object.data.flags.dnd5e || {};\n\n // Add feat-related proficiency bonuses\n if ( feats.remarkableAthlete && DND5E.characterFlags.remarkableAthlete.abilities.includes(abilityId) ) {\n parts.push(\"@proficiency\");\n data.proficiency = Math.ceil(0.5 * this.data.data.attributes.prof);\n }\n else if ( feats.jackOfAllTrades ) {\n parts.push(\"@proficiency\");\n data.proficiency = Math.floor(0.5 * this.data.data.attributes.prof);\n }\n\n // Add global actor bonus\n let actorBonus = getProperty(app.object.data.data.bonuses, \"abilities.check\");\n if ( !!actorBonus ) {\n parts.push(\"@checkBonus\");\n data.checkBonus = actorBonus;\n\t\t}\n\t\t\n\t\tdata.parts = parts;\n\n\t\tdata.title = game.i18n.format(\"DND5E.AbilityPromptTitle\", {ability: label});\n\n\t\trollD20.bind(app.object)(data);\n\t\treturn true;\n\t}));\n\tconst saveMods = html[0].querySelectorAll('.ability-save');\n\tsaveMods.forEach(e => e.addEventListener('click', function(ev) {\n\t\tev.stopPropagation();\n\t\tev.preventDefault();\n\t\tconst abilityId = ev.currentTarget.closest('.ability').dataset.ability;\n\t\tconst label = CONFIG.DND5E.abilities[abilityId];\n const abl = app.object.data.data.abilities[abilityId];\n const parts = [\"@mod\"];\n const data = {mod: abl.mod};\n\n // Include proficiency bonus\n if ( abl.prof > 0 ) {\n parts.push(\"@prof\");\n data.prof = abl.prof;\n }\n\n // Include a global actor ability save bonus\n const actorBonus = getProperty(app.object.data.data.bonuses, \"abilities.save\");\n if ( !!actorBonus ) {\n parts.push(\"@saveBonus\");\n data.saveBonus = actorBonus;\n }\n\t\tdata.title = game.i18n.format(\"DND5E.SavePromptTitle\", {ability: label});\n\t\tdata.parts = parts;\n\t\trollD20.bind(app.object)(data);\n\t}));\n\n\tconst skills = html[0].querySelectorAll('.skill-name');\n\t$(skills).off();\n\tskills.forEach(e => e.addEventListener('click', function(ev) {\n\t\tev.stopPropagation();\n\t\tev.preventDefault();\n\t\tconst skillId = ev.currentTarget.closest('.skill').dataset.skill;\n\t\tconst skl = app.object.data.data.skills[skillId];\n\n // Compose roll parts and data\n const parts = [\"@mod\"];\n const data = {mod: skl.mod + skl.prof};\n if ( skl.bonus ) {\n data[\"skillBonus\"] = skl.bonus;\n parts.push(\"@skillBonus\");\n }\n\n // Reliable Talent applies to any skill check we have full or better proficiency in\n const reliableTalent = (skl.value >= 1 && app.object.getFlag(\"dnd5e\", \"reliableTalent\"));\n\t\tdata.parts = parts;\n\t\tdata.title = game.i18n.format(\"DND5E.SkillPromptTitle\", {skill: CONFIG.DND5E.skills[skillId]});\n\n\t\trollD20.bind(app.object)(data);\n\t\treturn false;\n\t}))\n}\n\n\nexport default async function modifyRolling() {\n\tgame.settings.register('mess', `${game.userId}.adv-selector`, {\n\t\tname: 'Mess - Advantage Selector',\n\t\tdefault: 'normal',\n\t\ttype: String,\n\t\tscope: 'user'\n\t});\n\tgame.settings.register('mess', `${game.userId}.autoroll-selector`, {\n\t\tname: 'Mess - Autoroll Selector',\n\t\tdefault: {hit: false, dmg: false},\n\t\ttype: Object,\n\t\tscope: 'user'\n\t});\n\n\t// possible that this function g ets called *after* chatLog creation, so check if its there already, and if yes work with the existing one.\n\t// this needs further investigating\n\tHooks.on('renderChatLog', async (app, html, data) => {\n\t\tconst div = await createControls();\n\t\tconst controls = html[0].querySelector('#chat-controls');\n\t\tcontrols.insertBefore(div, controls.childNodes[0]);\n\t});\n\n\t// roundabout way to get the listener do what *I* want...\n\t// Since adding my own listener in renderChatLog was \"to early\".\n\tCONFIG.Item.entityClass.chatListeners = function (html) {\n html.on('click', '.card-buttons button', onChatCardAction.bind(this));\n\t\thtml.on('click', '.item-name', this._onChatCardToggleContent.bind(this));\n\t\t\n\t\t// lets just use this for even more listeners\n\t\thtml.on('mouseenter', '.mess-chat-target', onMouseEnterTarget);\n\t\thtml.on('mouseleave', '.mess-chat-target', onMouseLeaveTarget);\n\t\thtml.on('dblclick', '.mess-chat-target', onDblClickTarget);\n\n\t\thtml.on('click', '.mess-button-to-hit', rollHit);\n\t\thtml.on('click', '.mess-button-dmg', rollDmg);\n\t}\n\n\tHooks.on('preCreateChatMessage', async (data) => {\n\t\tconst div = document.createElement('div');\n\t\tdiv.insertAdjacentHTML('afterbegin', data.content);\n\t\tlet btn = div.querySelector('button[data-action=\"attack\"]');\n\t\tif (!btn) {\n\t\t\tbtn = div.querySelector('button[data-action=\"damage\"]');\n\t\t}\n\t\tif (btn)\n\t\t\trenderAttack({currentTarget: btn});\n\n\t});\n\n\tHooks.on('renderItemSheet', itemHook)\n\n\tHooks.on('renderActorSheet', actorSheetHook)\n\n\tchangeAbilityTemplate();\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/bundles/modify-templates.26bb.js b/dist/scripts/bundles/modify-templates.26bb.js deleted file mode 100644 index fb185aa..0000000 --- a/dist/scripts/bundles/modify-templates.26bb.js +++ /dev/null @@ -1,6 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["modify-templates"],{"./src/scripts/modify-templates.js": -/*!*****************************************!*\ - !*** ./src/scripts/modify-templates.js ***! - \*****************************************/ -/*! exports provided: default */function(t,e,a){"use strict";function s(){let t=MeasuredTemplate.prototype.refresh.toString().replace(/this\.template\.beginTextureFill\(\{[\s\S]*\}\)\;/,'{\n let mat = PIXI.Matrix.IDENTITY;\n // rectangle\n if (this.shape.width && this.shape.height)\n mat.scale(this.shape.width / this.texture.width, this.shape.height / this.texture.height);\n else if (this.shape.radius) {\n mat.scale(this.shape.radius * 2 / this.texture.height, this.shape.radius * 2 / this.texture.width)\n // Circle center is texture start...\n mat.translate(-this.shape.radius, -this.shape.radius);\n } else if (this.data.t === "ray") {\n const d = canvas.dimensions,\n height = this.data.width * d.size / d.distance,\n width = this.data.distance * d.size / d.distance;\n mat.scale(width / this.texture.width, height / this.texture.height);\n mat.translate(0, -height * 0.5);\n\n mat.rotate(toRadians(this.data.direction));\n } else {// cone\n const d = canvas.dimensions;\n \n // Extract and prepare data\n let {direction, distance, angle} = this.data;\n distance *= (d.size / d.distance);\n direction = toRadians(direction);\n const width = this.data.distance * d.size / d.distance;\n\n const angles = [(angle/-2), (angle/2)];\n distance = distance * Math.cos(toRadians(angle/2));\n \n // Get the cone shape as a polygon\n const rays = angles.map(a => Ray.fromAngle(0, 0, direction + toRadians(a), distance+1));\n const height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x)\n + (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y));\n mat.scale(width / this.texture.width, height / this.texture.height);\n mat.translate(0, -height/2)\n mat.rotate(toRadians(this.data.direction));\n }\n this.template.beginTextureFill({\n texture: this.texture,\n matrix: mat,\n alpha: 0.8\n });\n // move into draw or so\n const source = getProperty(this.texture, "baseTexture.resource.source")\n if ( source && (source.tagName === "VIDEO") ) {\n source.loop = true;\n source.muted = true;\n game.video.play(source);\n }\n }');MeasuredTemplate.prototype.refresh=Function(`"use strict"; return ( function ${t} )`)(),Hooks.on("renderMeasuredTemplateConfig",(t,e,a)=>{e[0].querySelector(".file-picker").dataset.type="imagevideo"})}a.r(e),a.d(e,"default",(function(){return s}))}}]); -//# sourceMappingURL=modify-templates.26bb.js.map \ No newline at end of file diff --git a/dist/scripts/bundles/modify-templates.26bb.js.map b/dist/scripts/bundles/modify-templates.26bb.js.map deleted file mode 100644 index be050cd..0000000 --- a/dist/scripts/bundles/modify-templates.26bb.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/scripts/modify-templates.js"],"names":["changeTemplateFill","newFun","MeasuredTemplate","prototype","refresh","toString","replace","Function","Hooks","on","app","html","data","querySelector","dataset","type"],"mappings":";;;;6DAAe,SAASA,IAKtB,IAEIC,EAFSC,iBAAiBC,UAAUC,QAAQC,WAE5BC,QAAQ,oDAAqD,moEAkDjFJ,iBAAiBC,UAAUC,QAAUG,SAAS,mCAAmCN,MAA5CM,GAErCC,MAAMC,GAAG,+BAAgC,CAACC,EAAKC,EAAMC,KACnDD,EAAK,GAAGE,cAAc,gBAAgBC,QAAQC,KAAO,eA5DzD","file":"bundles/modify-templates.26bb.js","sourcesContent":["export default function changeTemplateFill() {\n\n // #MonkeyPatchingFTW\n // better than stealing the code, replacing one line and then release it under a/the wrong license..\n // Disadvantage: could need more fixing on updates. At least i didn#t make it line based like Kakaroto.. :P\n let oldFun = MeasuredTemplate.prototype.refresh.toString();\n \n let newFun = oldFun.replace(/this\\.template\\.beginTextureFill\\(\\{[\\s\\S]*\\}\\)\\;/, `{\n let mat = PIXI.Matrix.IDENTITY;\n // rectangle\n if (this.shape.width && this.shape.height)\n mat.scale(this.shape.width / this.texture.width, this.shape.height / this.texture.height);\n else if (this.shape.radius) {\n mat.scale(this.shape.radius * 2 / this.texture.height, this.shape.radius * 2 / this.texture.width)\n // Circle center is texture start...\n mat.translate(-this.shape.radius, -this.shape.radius);\n } else if (this.data.t === \"ray\") {\n const d = canvas.dimensions,\n height = this.data.width * d.size / d.distance,\n width = this.data.distance * d.size / d.distance;\n mat.scale(width / this.texture.width, height / this.texture.height);\n mat.translate(0, -height * 0.5);\n\n mat.rotate(toRadians(this.data.direction));\n } else {// cone\n const d = canvas.dimensions;\n \n // Extract and prepare data\n let {direction, distance, angle} = this.data;\n distance *= (d.size / d.distance);\n direction = toRadians(direction);\n const width = this.data.distance * d.size / d.distance;\n\n const angles = [(angle/-2), (angle/2)];\n distance = distance * Math.cos(toRadians(angle/2));\n \n // Get the cone shape as a polygon\n const rays = angles.map(a => Ray.fromAngle(0, 0, direction + toRadians(a), distance+1));\n const height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x)\n + (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y));\n mat.scale(width / this.texture.width, height / this.texture.height);\n mat.translate(0, -height/2)\n mat.rotate(toRadians(this.data.direction));\n }\n this.template.beginTextureFill({\n texture: this.texture,\n matrix: mat,\n alpha: 0.8\n });\n // move into draw or so\n const source = getProperty(this.texture, \"baseTexture.resource.source\")\n if ( source && (source.tagName === \"VIDEO\") ) {\n source.loop = true;\n source.muted = true;\n game.video.play(source);\n }\n }`);\n MeasuredTemplate.prototype.refresh = Function(`\"use strict\"; return ( function ${newFun} )`)();\n\n Hooks.on('renderMeasuredTemplateConfig', (app, html, data) => {\n html[0].querySelector('.file-picker').dataset.type = 'imagevideo'\n });\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/bundles/prepared-spell-tracker.dee3.js b/dist/scripts/bundles/prepared-spell-tracker.dee3.js deleted file mode 100644 index a157293..0000000 --- a/dist/scripts/bundles/prepared-spell-tracker.dee3.js +++ /dev/null @@ -1,6 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["prepared-spell-tracker"],{"./src/scripts/prepared-spell-tracker.js": -/*!***********************************************!*\ - !*** ./src/scripts/prepared-spell-tracker.js ***! - \***********************************************/ -/*! exports provided: default */function(e,t,r){"use strict";async function n(){Hooks.on("renderActorSheet",(async function(e,t,r){const n=await fromUuid("Actor."+r.actor._id),s=t[0].querySelector(".spell-dc");if(!s)return;let a=document.createElement("div");a.classList.add("spell-slots"),a.style.flex="1",a.style.border="none";const l=a.appendChild(document.createElement("span"));l.innerText="prepared spells: "+r.preparedSpells,l.classList.add("spell-max");const p=a.appendChild(document.createElement("span"));p.innerText=" / ",p.classList.add("sep");const c=a.appendChild(document.createElement("input"));c.type="text",c.value=n.getFlag("mess","maxPreparedSpells")||0,c.addEventListener("change",(async function(e){e.preventDefault(),e.stopPropagation();const t=Number(e.currentTarget.value);return isNaN(t)?(e.currentTarget.value=n.getFlag("mess","maxPreparedSpells")||0,!1):(n.setFlag("mess","maxPreparedSpells",t),!1)})),s.parentNode.insertBefore(a,s)}))}r.r(t),r.d(t,"default",(function(){return n}))}}]); -//# sourceMappingURL=prepared-spell-tracker.dee3.js.map \ No newline at end of file diff --git a/dist/scripts/bundles/prepared-spell-tracker.dee3.js.map b/dist/scripts/bundles/prepared-spell-tracker.dee3.js.map deleted file mode 100644 index 02b6c46..0000000 --- a/dist/scripts/bundles/prepared-spell-tracker.dee3.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/scripts/prepared-spell-tracker.js"],"names":["async","addPreparedSpellTracker","Hooks","on","app","html","data","actor","fromUuid","_id","spellDc","querySelector","tracker","document","createElement","classList","add","style","flex","border","spanPrep","appendChild","innerText","preparedSpells","sep","max","type","value","getFlag","addEventListener","ev","preventDefault","stopPropagation","val","Number","currentTarget","isNaN","setFlag","parentNode","insertBefore"],"mappings":";;;;6DAAeA,eAAeC,IAC7BC,MAAMC,GAAG,oBAAoBH,eAAgBI,EAAMC,EAAMC,GACxD,MAAMC,QAAcC,SAAS,SAASF,EAAKC,MAAME,KAC3CC,EAAUL,EAAK,GAAGM,cAAc,aACtC,IAAKD,EAAS,OACd,IAAIE,EAAUC,SAASC,cAAc,OACrCF,EAAQG,UAAUC,IAAI,eACtBJ,EAAQK,MAAMC,KAAO,IACrBN,EAAQK,MAAME,OAAS,OACvB,MAAMC,EAAWR,EAAQS,YAAYR,SAASC,cAAc,SAC5DM,EAASE,UAAY,oBAAoBhB,EAAKiB,eAC9CH,EAASL,UAAUC,IAAI,aAEvB,MAAMQ,EAAMZ,EAAQS,YAAYR,SAASC,cAAc,SACvDU,EAAIF,UAAY,MAChBE,EAAIT,UAAUC,IAAI,OAElB,MAAMS,EAAMb,EAAQS,YAAYR,SAASC,cAAc,UACvDW,EAAIC,KAAO,OACXD,EAAIE,MAAQpB,EAAMqB,QAAQ,OAAQ,sBAAwB,EAC1DH,EAAII,iBAAiB,UAAU7B,eAAe8B,GAC7CA,EAAGC,iBACHD,EAAGE,kBACH,MAAMC,EAAMC,OAAOJ,EAAGK,cAAcR,OACpC,OAAIS,MAAMH,IACTH,EAAGK,cAAcR,MAAQpB,EAAMqB,QAAQ,OAAQ,sBAAwB,GAChE,IAGRrB,EAAM8B,QAAQ,OAAQ,oBAAqBJ,IACpC,MAERvB,EAAQ4B,WAAWC,aAAa3B,EAASF,MAhC3C","file":"bundles/prepared-spell-tracker.dee3.js","sourcesContent":["export default async function addPreparedSpellTracker() {\n\tHooks.on('renderActorSheet', async function (app, html, data) {\n\t\tconst actor = await fromUuid(`Actor.${data.actor._id}`);\n\t\tconst spellDc = html[0].querySelector('.spell-dc');\n\t\tif (!spellDc) return;\n\t\tlet tracker = document.createElement('div');\n\t\ttracker.classList.add('spell-slots');\n\t\ttracker.style.flex = '1';\n\t\ttracker.style.border = 'none';\n\t\tconst spanPrep = tracker.appendChild(document.createElement('span'));\n\t\tspanPrep.innerText = `prepared spells: ${data.preparedSpells}`;\n\t\tspanPrep.classList.add('spell-max');\n\n\t\tconst sep = tracker.appendChild(document.createElement('span'));\n\t\tsep.innerText = ' / ';\n\t\tsep.classList.add('sep');\n\n\t\tconst max = tracker.appendChild(document.createElement('input'));\n\t\tmax.type = 'text';\n\t\tmax.value = actor.getFlag('mess', 'maxPreparedSpells') || 0;\n\t\tmax.addEventListener('change', async function(ev) {\n\t\t\tev.preventDefault();\n\t\t\tev.stopPropagation();\n\t\t\tconst val = Number(ev.currentTarget.value);\n\t\t\tif (isNaN(val)) {\n\t\t\t\tev.currentTarget.value = actor.getFlag('mess', 'maxPreparedSpells') || 0;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tactor.setFlag('mess', 'maxPreparedSpells', val);\n\t\t\treturn false;\n\t\t});\n\t\tspellDc.parentNode.insertBefore(tracker, spellDc);\n\t});\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/bundles/welcome-screen.89ea.js b/dist/scripts/bundles/welcome-screen.89ea.js new file mode 100644 index 0000000..d85642b --- /dev/null +++ b/dist/scripts/bundles/welcome-screen.89ea.js @@ -0,0 +1,6 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([["welcome-screen"],{"./src/scripts/welcome-screen.js": +/*!***************************************!*\ + !*** ./src/scripts/welcome-screen.js ***! + \***************************************/ +/*! exports provided: default */function(e,t,s){"use strict";function n(){const e=game.modules.get("mess"),t=e.id,s=e.data.title,n=e.data.version;game.settings.register(s,"version",{name:s+" Version",default:"0.0.0",type:String,scope:"world"});const i=game.settings.get(s,"version");if(isNewerVersion(n,i)){class e extends Application{static get defaultOptions(){const e=super.defaultOptions;return e.template=`modules/${t}/templates/welcome-screen.html`,e.resizable=!0,e.width=450,e.height=600,e.classes=["welcome-screen"],e.title=s+" - Welcome Screen",e}activateListeners(e){super.activateListeners(e),e.find(".show-again").on("change",e=>{let t="0.0.0";e.currentTarget.checked&&(t=n),game.settings.set(s,"version",t)})}}(new e).render(!0)}}s.r(t),s.d(t,"default",(function(){return n}))}}]); +//# sourceMappingURL=welcome-screen.89ea.js.map \ No newline at end of file diff --git a/dist/scripts/bundles/welcome-screen.89ea.js.map b/dist/scripts/bundles/welcome-screen.89ea.js.map new file mode 100644 index 0000000..492b120 --- /dev/null +++ b/dist/scripts/bundles/welcome-screen.89ea.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./src/scripts/welcome-screen.js"],"names":["renderWelcomeScreen","module","game","modules","get","moduleId","id","title","data","moduleVersion","version","settings","register","name","default","type","String","scope","oldVersion","isNewerVersion","WelcomeScreen","Application","options","super","defaultOptions","template","resizable","width","height","classes","html","activateListeners","find","on","ev","val","currentTarget","checked","set","render"],"mappings":";;;;6DAAe,SAASA,IACvB,MAAMC,EAASC,KAAKC,QAAQC,IAAI,QAC1BC,EAAWJ,EAAOK,GAClBC,EAAQN,EAAOO,KAAKD,MACpBE,EAAgBR,EAAOO,KAAKE,QAClCR,KAAKS,SAASC,SAASL,EAAO,UAAW,CACxCM,KAASN,EAAH,WACNO,QAAS,QACTC,KAAMC,OACNC,MAAO,UAER,MAAMC,EAAahB,KAAKS,SAASP,IAAIG,EAAO,WAE5C,GAAKY,eAAeV,EAAeS,GAAnC,CAGA,MAAME,UAAsBC,YAC3B,4BACC,MAAMC,EAAUC,MAAMC,eAOtB,OANAF,EAAQG,SAAW,WAAWpB,kCAC9BiB,EAAQI,WAAY,EACpBJ,EAAQK,MAAQ,IAChBL,EAAQM,OAAS,IACjBN,EAAQO,QAAU,CAAC,kBACnBP,EAAQf,MAAWA,EAAH,oBACTe,EAGR,kBAAkBQ,GACjBP,MAAMQ,kBAAkBD,GAExBA,EAAKE,KAAK,eAAeC,GAAG,SAAUC,IACrC,IAAIC,EAAM,QACND,EAAGE,cAAcC,UACpBF,EAAM1B,GAEPP,KAAKS,SAAS2B,IAAI/B,EAAO,UAAW4B,OAKvC,IAAKf,GAAiBmB,QAAO,IAzC9B","file":"bundles/welcome-screen.89ea.js","sourcesContent":["export default function renderWelcomeScreen() {\n\tconst module = game.modules.get(\"mess\");\n\tconst moduleId = module.id;\n\tconst title = module.data.title;\n\tconst moduleVersion = module.data.version;\n\tgame.settings.register(title, 'version', {\n\t\tname: `${title} Version`,\n\t\tdefault: \"0.0.0\",\n\t\ttype: String,\n\t\tscope: 'world',\n\t});\n\tconst oldVersion = game.settings.get(title, \"version\");\n\n\tif (!isNewerVersion(moduleVersion, oldVersion))\n\t\treturn;\n\n\tclass WelcomeScreen extends Application {\n\t\tstatic get defaultOptions() {\n\t\t\tconst options = super.defaultOptions;\n\t\t\toptions.template = `modules/${moduleId}/templates/welcome-screen.html`;\n\t\t\toptions.resizable = true;\n\t\t\toptions.width = 450;\n\t\t\toptions.height = 600;\n\t\t\toptions.classes = [\"welcome-screen\"];\n\t\t\toptions.title = `${title} - Welcome Screen`;\n\t\t\treturn options;\n\t\t}\n\n\t\tactivateListeners(html) {\n\t\t\tsuper.activateListeners(html);\n\n\t\t\thtml.find('.show-again').on('change', ev => {\n\t\t\t\tlet val = \"0.0.0\";\n\t\t\t\tif (ev.currentTarget.checked)\n\t\t\t\t\tval = moduleVersion;\n\n\t\t\t\tgame.settings.set(title, \"version\", val);\n\t\t\t})\n\t\t}\n\t}\n\n\t(new WelcomeScreen()).render(true);\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/scripts/index.js b/dist/scripts/index.js index 3da436f..ea7e4f3 100644 --- a/dist/scripts/index.js +++ b/dist/scripts/index.js @@ -1,6 +1,50 @@ -!function(e){function t(t){for(var s,n,a=t[0],i=t[1],o=0,c=[];o{const t=e.closest(".tab").dataset.tab,s=document.createElement("a");s.innerHTML='',s.classList.add("mess-sort-btn"),s.title=`Sort ${t} alphabetically.`,s.style.flex=0,s.style.margin="0 5px 0 0",s.addEventListener("click",e=>async function(e,t){const a=await fromUuid("Actor."+t);let s=e.map(e=>e.items||e.spells).flat();s.sort((function(e,t){return e.namet.name?1:0}));let n=[s.shift()],r=[];for(;s.length>0;n.push(s.shift()))r=SortingHelpers.performIntegerSort(s[0],{target:n[n.length-1],siblings:duplicate(n),sortBefore:!1});const o=r.map(e=>{let t=e.update;return t._id=e.target._id,t});a.updateEmbeddedEntity("OwnedItem",o)}(a[t],a.actor._id)),e.prepend(s)})}async function n(){Hooks.on("renderActorSheet",(e,t,a)=>{s(0,t[0],a)})}a.r(t),a.d(t,"default",(function(){return n}))},"./src/scripts/add-scrolling.js": +/*!**************************************!*\ + !*** ./src/scripts/add-scrolling.js ***! + \**************************************/ +/*! exports provided: default */function(e,t,a){"use strict";async function s(){Hooks.on("renderActorSheet",(async function(e,t,a){t[0].querySelectorAll('input[data-dtype="Number"], .item-uses input').forEach(e=>{e.addEventListener("wheel",e=>{e.preventDefault(),e.stopPropagation(),e.deltaY<0&&(e.currentTarget.value=Number(e.currentTarget.value)+1),e.deltaY>0&&(e.currentTarget.value=Math.max(Number(e.currentTarget.value)-1,0)),$(e.currentTarget).change()})})}))}a.r(t),a.d(t,"default",(function(){return s}))},"./src/scripts/change-placeables.js": +/*!******************************************!*\ + !*** ./src/scripts/change-placeables.js ***! + \******************************************/ +/*! exports provided: changePlaceables */function(e,t,a){"use strict";function s(e){const{clones:t,destination:a,origin:s,originalEvent:n}=e.data;canvas._onDragCanvasPan(n);const r=a.x-s.x,o=a.y-s.y;let i=!1;if(e.data.previous){const t=30,s=.8,n=e.data.previous,r=e.data.momentum||0,o=e.data.v||{x:0,y:0},l={x:a.x-n.x,y:a.y-n.y},c={x:l.x-o.x,y:l.y-o.y};e.data.momentum=c.x*c.x+c.y*c.y+r*s,i=!e.shiftKey&&e.data.momentum Ray.fromAngle(0, 0, direction + toRadians(a), distance+1));\n const height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x)\n + (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y));\n mat.scale(width / this.texture.width, height / this.texture.height);\n mat.translate(0, -height/2)\n mat.rotate(toRadians(this.data.direction));\n }\n this.template.beginTextureFill({\n texture: this.texture,\n matrix: mat,\n alpha: 0.8\n });\n // move into draw or so\n const source = getProperty(this.texture, "baseTexture.resource.source")\n if ( source && (source.tagName === "VIDEO") ) {\n source.loop = true;\n source.muted = true;\n game.video.play(source);\n }\n }');MeasuredTemplate.prototype.refresh=Function(`"use strict"; return ( function ${e} )`)(),Hooks.on("renderMeasuredTemplateConfig",(e,t,a)=>{t[0].querySelector(".file-picker").dataset.type="imagevideo"})}async function n(){if("dnd5e"!==game.system.id)return;Hooks.on("renderItemSheet",i);const e=await import("/systems/dnd5e/module/pixi/ability-template.js"),t=e.default||e.AbilityTemplate,a=t.fromItem;t.fromItem=function(e){const t=a.bind(this)(e);let s=e.getFlag("mess","templateTexture");if(!s&&e.hasDamage){const a=game.settings.get("mess","templateTexture")||{};s=a[e.data.data.damage.parts[0][1]]||{},s=s[t.data.t]}return s&&loadTexture(s).then(e=>{t.texture=e,t.data.texture=s,t.refresh()}),t.item=e,t};const s=t.prototype.activatePreviewListeners.toString().replace(/this\.refresh\(\)\;/,"this.refresh();\n\t\t\t\t\tthis.getTargets(this);\n\t\t\t\t");t.prototype.getTargets=o,t.prototype.isTokenInside=r,t.prototype.activatePreviewListeners=Function(`"use strict"; return ( function ${s} )`)()}function r(e){const t=canvas.scene.data.grid,a=this.data.x,s=this.data.y,n=e.width>=1?.5:e.width/2,r=e.height>=1?.5:e.height/2;for(let o=n;o\n\t\t\t\n\t\t\n\t');const r=s.querySelector("button");r.style.flex="0",e._activateFilePicker(r);const o=t[0].querySelector('[name="data.target.units"]');o&&o.closest(".form-group").after(a)}a.r(t),a.d(t,"changeTemplateFill",(function(){return s})),a.d(t,"dndTemplateSettings",(function(){return n}))},"./src/scripts/prepared-spell-tracker.js": +/*!***********************************************!*\ + !*** ./src/scripts/prepared-spell-tracker.js ***! + \***********************************************/ +/*! exports provided: default */function(e,t,a){"use strict";async function s(){Hooks.on("renderActorSheet",(async function(e,t,a){const s=await fromUuid("Actor."+a.actor._id),n=t[0].querySelector(".spell-dc");if(!n)return;let r=document.createElement("div");r.classList.add("spell-slots"),r.style.flex="1",r.style.border="none";const o=r.appendChild(document.createElement("span"));o.innerText="prepared spells: "+a.preparedSpells,o.classList.add("spell-max");const i=r.appendChild(document.createElement("span"));i.innerText=" / ",i.classList.add("sep");const l=r.appendChild(document.createElement("input"));l.type="text",l.value=s.getFlag("mess","maxPreparedSpells")||0,l.addEventListener("change",(async function(e){e.preventDefault(),e.stopPropagation();const t=Number(e.currentTarget.value);return isNaN(t)?(e.currentTarget.value=s.getFlag("mess","maxPreparedSpells")||0,!1):(s.setFlag("mess","maxPreparedSpells",t),!1)})),n.parentNode.insertBefore(r,n)}))}a.r(t),a.d(t,"default",(function(){return s}))},"./src/scripts/rolls/apply-dmg.js": +/*!****************************************!*\ + !*** ./src/scripts/rolls/apply-dmg.js ***! + \****************************************/ +/*! exports provided: default */function(e,t,a){"use strict";function s(){Hooks.on("getChatLogEntryContext",(function(e,t){setProperty(game,"mess.chatLogEntryContextOptions",t);const a=e=>canvas.tokens.controlled.length&&e.find(".dice-roll .dice-result").length;for(let e=1;e{e.addEventListener("click",(async function(e){e.preventDefault(),e.stopPropagation();const t=Array.from(e.currentTarget.parentNode.querySelectorAll("a")),a=t.findIndex(e=>e.classList.contains("mess-selected"));t[a].classList.remove("mess-selected");const s=t[(a+1)%t.length];s.classList.add("mess-selected"),game.settings.set("mess",game.userId+".adv-selector",s.name)})),e.addEventListener("contextmenu",(async function(e){e.preventDefault(),e.stopPropagation();const t=Array.from(e.currentTarget.parentNode.querySelectorAll("a")),a=t.findIndex(e=>e.classList.contains("mess-selected"));t[a].classList.remove("mess-selected");const s=t[(a+t.length-1)%t.length];s.classList.add("mess-selected"),game.settings.set("mess",game.userId+".adv-selector",s.name)}))}),s.querySelectorAll(".mess-autoroll-selector a").forEach(e=>{e.addEventListener("click",(async function(e){e.preventDefault(),e.stopPropagation(),e.currentTarget.classList.toggle("mess-selected");let t=game.settings.get("mess",game.userId+".autoroll-selector");t[e.currentTarget.name]=e.currentTarget.classList.contains("mess-selected"),game.settings.set("mess",game.userId+".autoroll-selector",t)}))});const o=document.getElementById("chat-controls");o.insertBefore(s,o.childNodes[0])}a.r(t),t.default=function(){Hooks.on("renderChatLog",s),game.settings.register("mess",game.userId+".adv-selector",{name:"Mess - Advantage Selector",default:"normal",type:String,scope:"user"}),game.settings.register("mess",game.userId+".autoroll-selector",{name:"Mess - Autoroll Selector",default:{hit:!1,dmg:!1},type:Object,scope:"user"})}},"./src/scripts/rolls/dice.js": +/*!***********************************!*\ + !*** ./src/scripts/rolls/dice.js ***! + \***********************************/ +/*! exports provided: rollD20, getToHitData, rollToHit, getDmgData, rollDmg */function(e,t,a){"use strict";function s(){return document.getElementById("mess-roll-mod").value}function n(){return game.settings.get("mess",game.userId+".adv-selector")}async function r(e){let t=n(),a=1,r=e.halflingLucky?"r=1":"";"advantage"===t?(a=e.elvenAccuracy?3:2,r+="kh",e.title+=` (${game.i18n.localize("DND5E.Advantage")})`):"disadvantage"===t&&(a=2,r+="kl",e.title+=` (${game.i18n.localize("DND5E.Disadvantage")})`);let o=`${a}d20${r}`;e.reliableTalent&&(o=`{${a}d20${r},10}kh`),e.parts.unshift(o);const i=s();i&&e.parts.push(i);let l=new Roll(e.parts.join("+"),e);l.roll();const c=l.parts[0].total;let d={...e,tooltip:await l.getTooltip(),roll:l,crit:c>=20,fumble:c<=1};const u=await renderTemplate("modules/mess/templates/roll-card.html",d);let m={user:game.user._id,type:CONST.CHAT_MESSAGE_TYPES.OTHER,content:u,speaker:{actor:this,alias:this.name}},g=game.settings.get("core","rollMode");["gmroll","blindroll"].includes(g)&&(m.whisper=ChatMessage.getWhisperIDs("GM")),"blindroll"===g&&(m.blind=!0),ChatMessage.create(m)}async function o({actor:e,item:t}){if(!t.hasAttack)return null;const a=e.data.data,n=t.data.data,r=e.data.flags.dnd5e||{};let o=t.getRollData();const i=["@mod"];("weapon"!==t.data.type||n.proficient)&&i.push("@prof"),o.parts=i,"weapon"===t.data.type&&r.weaponCriticalThreshold&&(o.critical=parseInt(r.weaponCriticalThreshold)),["weapon","spell"].includes(t.data.type)&&r.elvenAccuracy&&["dex","int","wis","cha"].includes(t.abilityMod)&&(o.elvenAccuracy=!0),r.halflingLucky&&(o.halflingLucky=!0);const l=a.bonuses[n.actionType]||{};(n.attackBonus||l.attack)&&(o.atk=[n.attackBonus,l.attack].filterJoin(" + "),isNaN(Number(o.atk))||i.push("@atk"));let c=new Roll(o.parts.join("+"),o);o.totalModifier=c._safeEval(c.formula),o.totalModifier=o.totalModifier>=0?"+"+o.totalModifier:o.totalModifier,o.atk&&!c._formula.includes("@atk")&&(o.parts.push("@atk"),c=new Roll(o.parts.join("+"),o),o.totalModifier+="+"+o.atk);const d=s();return d&&(o.parts.push(d),c=new Roll(o.parts.join("+"),o),o.totalModifier+="+"+d),o.formula=c.formula,o.terms=c._formula,o}async function i(e){const t=e.currentTarget;t.disabled=!0;const a=t.closest(".chat-card"),s=a.closest(".message").dataset.messageId;if(s){const e=game.messages.get(s);if(!e.owner&&!e.isAuthor)return void ui.notifications.error("You do not own the permissions to make that roll!")}const r=CONFIG.Item.entityClass._getChatCardActor(a);if(!r.owner)return!1;const i=r.getOwnedItem(a.dataset.itemId);if(!i)return ui.notifications.error(`The requested item ${a.dataset.itemId} no longer exists on Actor ${r.name}`);let l=await o({actor:r,item:i}),c=n(),d=1,u=l.halflingLucky?"r=1":"";"advantage"===c?(d=l.elvenAccuracy?3:2,u+="kh"):"disadvantage"===c&&(d=2,u+="kl"),l.parts.unshift(`${d}d20${u}`);let m=new Roll(l.parts.join("+"),l);m.roll();let g=document.createElement("div");g.title=`${l.parts[0]}+${l.terms} = ${m.formula} = ${m.total}. Click to see rolls.`,g.classList.add("dice-roll"),g.classList.add("mess-dice-result");const p=g.appendChild(document.createElement("span"));p.innerText=m.total,g.insertAdjacentHTML("beforeend",await m.getTooltip()),g.childNodes[1].classList.add("hidden");const f=l.critical||20,h=l.fumble||1,y=m.parts[0].total;if(y>=f&&(p.classList.add("crit"),a.querySelector(".mess-chat-dmg .mess-chat-roll-type").innerHTML+=" - Crit!",a.querySelectorAll(".mess-button-dmg").forEach((e,t)=>{const a=e.dataset.formula,s=new Roll(a);s.alter(0,2),e.innerHTML=' '+s.formula,e.dataset.formula=s.formula})),y<=h&&p.classList.add("fumble"),e.currentTarget.parentNode.replaceChild(g,e.currentTarget),s){game.messages.get(s).update({content:a.parentNode.innerHTML})}}async function l({actor:e,item:t,spellLevel:a=null}){if(!t.hasDamage)return null;const s=e.data.data,n=t.data.data;let r=t.getRollData();if(a&&(r.item.level=a),r.parts=duplicate(n.damage.parts),n.damage.versatile&&r.parts.splice(1,0,[n.damage.versatile,"versatile"]),"spell"===t.data.type)if("cantrip"===n.scaling.mode){let a=[r.parts[0][0]];const o="character"===e.data.type?s.details.level:s.details.spellLevel;t._scaleCantripDamage(a,o,n.scaling.formula),r.parts[0][0]=a[0]}else if(a&&"level"===n.scaling.mode&&n.scaling.formula){let e=[];t._scaleSpellDamage(e,n.level,a,n.scaling.formula),e.length>0&&(e.push("upcast dice"),r.parts.push(e))}const o=s.bonuses[n.actionType]||{};o.damage&&0!==parseInt(o.damage)&&(parts[0][0]+="+@dmg",r.dmg=o.damage);for(let e of r.parts){let t=new Roll(e[0],r);CONFIG.DND5E.damageTypes[e[1]]?e[1]=game.i18n.localize("DND5E.Damage"+CONFIG.DND5E.damageTypes[e[1]]):"versatile"===e[1]&&(e[1]=game.i18n.localize("DND5E.Versatile")),e.push(t.formula)}return r}async function c(e){const t=e.currentTarget;t.disabled=!0;const a=t.closest(".chat-card"),s=a.closest(".message").dataset.messageId;if(s){const e=game.messages.get(s);if(!e.owner&&!e.isAuthor)return void ui.notifications.error("You do not own the permissions to make that roll!")}const n=t.dataset.formula;let r=new Roll(n);r.roll();let o=document.createElement("div");if(o.title=`${t.dataset.terms} = ${r.formula} = ${r.total}. Click to see rolls.`,o.classList.add("dice-roll"),o.classList.add("mess-dice-result"),o.appendChild(document.createElement("span")).innerText=r.total,o.insertAdjacentHTML("beforeend",await r.getTooltip()),o.childNodes[1].classList.add("hidden"),e.currentTarget.parentNode.replaceChild(o,e.currentTarget),s){game.messages.get(s).update({content:a.parentNode.innerHTML})}}a.r(t),a.d(t,"rollD20",(function(){return r})),a.d(t,"getToHitData",(function(){return o})),a.d(t,"rollToHit",(function(){return i})),a.d(t,"getDmgData",(function(){return l})),a.d(t,"rollDmg",(function(){return c}))},"./src/scripts/rolls/index.js": +/*!************************************!*\ + !*** ./src/scripts/rolls/index.js ***! + \************************************/ +/*! exports provided: rolling */function(e,t,a){"use strict";a.r(t),a.d(t,"rolling",(function(){return o}));var s=a(/*! ./controls.js */"./src/scripts/rolls/controls.js"),n=a(/*! ./apply-dmg.js */"./src/scripts/rolls/apply-dmg.js"),r=a(/*! ./modify-rolling.js */"./src/scripts/rolls/modify-rolling.js");function o(){game.settings.get("mess","modify-rolling")&&(Object(s.default)(),Object(n.default)(),Object(r.default)())}},"./src/scripts/rolls/modify-rolling.js": +/*!*********************************************!*\ + !*** ./src/scripts/rolls/modify-rolling.js ***! + \*********************************************/ +/*! exports provided: default */function(e,t,a){"use strict";a.r(t);var s=a(/*! ./dice.js */"./src/scripts/rolls/dice.js");async function n(e){const t=document.createElement("div");t.insertAdjacentHTML("afterbegin",e.content);let a=t.querySelector('button[data-action="attack"]');a||(a=t.querySelector('button[data-action="damage"]')),a&&r({currentTarget:a})}async function r(e){"click"===e.type&&(e.preventDefault(),e.stopPropagation());const t=e.currentTarget;t.disabled=!0;const a=t.closest(".chat-card"),n=CONFIG.Item.entityClass._getChatCardActor(a);if(!n||!n.owner)return;const r=n.getOwnedItem(a.dataset.itemId);if(!r)return ui.notifications.error(`The requested item ${a.dataset.itemId} no longer exists on Actor ${n.name}`);let i=game.user.targets;const l=Object.keys(CONFIG.DND5E.areaTargetTypes).includes(getProperty(r,"data.data.target.type"));i.size&&!l||(i=[{data:{name:"someone",img:""}}]);const c=parseInt(a.dataset.spellLevel)||null,d={actor:n,item:r,toHit:await Object(s.getToHitData)({actor:n,item:r}),dmgs:await Object(s.getDmgData)({actor:n,item:r,spellLevel:c}),sceneId:canvas.scene.id,user:game.user.id},u=game.settings.get("mess",game.userId+".autoroll-selector");let m=game.settings.get("core","rollMode");for(const e of i){const t=await r._handleResourceConsumption({isCard:!1,isAttack:!0}),a={...d,target:e.data,flavor:r.data.data.chatFlavor.replace(/\[target\.name\]/g,e.data.name),allowed:t};let s=await renderTemplate("modules/mess/templates/attack-card.html",a);(u.hit||u.dmg)&&(s=await o(u,s));let n={user:game.user._id,type:CONST.CHAT_MESSAGE_TYPES.OTHER,content:s,speaker:{actor:r.actor._id,token:r.actor.token,alias:r.actor.name}};["gmroll","blindroll"].includes(m)&&(n.whisper=ChatMessage.getWhisperIDs("GM")),"blindroll"===m&&(n.blind=!0),ChatMessage.create(n)}t.disabled=!1}async function o(e,t){let a=document.createElement("div");if(a.classList.add("message"),a.insertAdjacentHTML("afterbegin",t),e.hit){let e=a.querySelector(".mess-button-to-hit");e&&await Object(s.rollToHit)({currentTarget:e})}if(e.dmg){const e=Array.from(a.querySelectorAll(".mess-button-dmg"));for(const t of e)await Object(s.rollDmg)({currentTarget:t})}return a.innerHTML}async function i(e,t,a){const n=t[0].querySelectorAll(".ability-mod, .ability-name");$(n).off(),n.forEach(t=>t.addEventListener("click",(function(t){t.stopPropagation(),t.preventDefault();const a=t.currentTarget.closest(".ability").dataset.ability,n=CONFIG.DND5E.abilities[a],r=["@mod"],o={mod:e.object.data.data.abilities[a].mod},i=e.object.data.flags.dnd5e||{};i.remarkableAthlete&&DND5E.characterFlags.remarkableAthlete.abilities.includes(a)?(r.push("@proficiency"),o.proficiency=Math.ceil(.5*this.data.data.attributes.prof)):i.jackOfAllTrades&&(r.push("@proficiency"),o.proficiency=Math.floor(.5*this.data.data.attributes.prof));let l=getProperty(e.object.data.data.bonuses,"abilities.check");return l&&(r.push("@checkBonus"),o.checkBonus=l),o.parts=r,o.title=game.i18n.format("DND5E.AbilityPromptTitle",{ability:n}),s.rollD20.bind(e.object)(o),!0}))),t[0].querySelectorAll(".ability-save").forEach(t=>t.addEventListener("click",(function(t){t.stopPropagation(),t.preventDefault();const a=t.currentTarget.closest(".ability").dataset.ability,n=CONFIG.DND5E.abilities[a],r=e.object.data.data.abilities[a],o=["@mod"],i={mod:r.mod};r.prof>0&&(o.push("@prof"),i.prof=r.prof);const l=getProperty(e.object.data.data.bonuses,"abilities.save");l&&(o.push("@saveBonus"),i.saveBonus=l),i.title=game.i18n.format("DND5E.SavePromptTitle",{ability:n}),i.parts=o,s.rollD20.bind(e.object)(i)})));const r=t[0].querySelectorAll(".skill-name");$(r).off(),r.forEach(t=>t.addEventListener("click",(function(t){t.stopPropagation(),t.preventDefault();const a=t.currentTarget.closest(".skill").dataset.skill,n=e.object.data.data.skills[a],r=["@mod"],o={mod:n.mod+n.prof};n.bonus&&(o.skillBonus=n.bonus,r.push("@skillBonus"));n.value>=1&&e.object.getFlag("dnd5e","reliableTalent");return o.parts=r,o.title=game.i18n.format("DND5E.SkillPromptTitle",{skill:CONFIG.DND5E.skills[a]}),s.rollD20.bind(e.object)(o),!1})))}function l(){const e=$(document.getElementById("chat-log"));e.on("click",".card-buttons button",c.bind(this)),e.on("click",".item-name",this._onChatCardToggleContent.bind(this)),e.on("mouseenter",".mess-chat-target",d),e.on("mouseleave",".mess-chat-target",u),e.on("dblclick",".mess-chat-target",m),e.on("click",".mess-button-to-hit",s.rollToHit),e.on("click",".mess-button-dmg",s.rollDmg)}async function c(e){return"attack"===e.currentTarget.dataset.action||"damage"===e.currentTarget.dataset.action?r(e):e.currentTarget.dataset.placeTemplate?renderTemplate(e):this._onChatCardAction(e)}async function d(e){e.preventDefault(),e.stopPropagation();const t=await g(e);if(!t)return!1;t._onHoverIn()}async function u(e){e.preventDefault(),e.stopPropagation();const t=await g(e);if(!t||!t.visible)return!1;t._onHoverOut()}async function m(e){e.preventDefault(),e.stopPropagation();const t=await g(e);if(!t||!t.visible)return!1;const a=t.center;canvas.animatePan({x:a.x,y:a.y})}async function g(e){const t=e.currentTarget.closest(".mess-attack-card");if(t.dataset.sceneId!==canvas.scene.id)return!1;const a=t.dataset.targetId;if(!a)return!1;const s=canvas.tokens.placeables.find(e=>e.id===a);return s||!1}t.default=function(){Hooks.on("preCreateChatMessage",n),Hooks.on("renderActorSheet",i),Hooks.on("ready",l.bind(CONFIG.Item.entityClass))}},"./src/scripts/settings.js": +/*!*********************************!*\ + !*** ./src/scripts/settings.js ***! + \*********************************/ +/*! exports provided: MessSettings */function(e,t,a){"use strict";a.r(t),a.d(t,"MessSettings",(function(){return s}));class s extends FormApplication{static init(){const e="dnd5e"===game.system.id;game.settings.register("mess","actor-item-sort",{name:"Activate item sort button.",hint:"Adds a button to actor sheets for sorting all items of that category alphabetically.",scope:"user",config:e,default:e,type:Boolean,onChange:()=>location.reload()}),game.settings.register("mess","better-draggable",{name:"Activate better drag'n'drop workflow.",scope:"user",config:!1,default:e,type:Boolean,onChange:()=>location.reload()}),game.settings.register("mess","prepared-spell-tracker",{name:"Activate prepared spell tracker",hint:"Adds a tracker to the spellbook tab, providing a way to track the allowed maximum of prepared spells.",scope:"user",config:e,default:e,type:Boolean,onChange:()=>location.reload()}),game.settings.register("mess","add-scrolling",{name:"Activating numerical field scrolling.",hint:"Lets you in-/decrease numerical fields in the Actor sheet using the mouse wheel when focused.",scope:"user",config:e,default:e,type:Boolean,onChange:()=>location.reload()}),game.settings.register("mess","modify-rolling",{name:"Alternative Rolling.",hint:"Changes the way rolling is displayed and executed for DnD5e. Reload for all connected clients is required for this to take effect if changed!",scope:"world",config:e,default:e,type:Boolean,onChange:()=>location.reload()}),game.settings.register("mess","modify-templates",{name:"Activate modified templates.",hint:"Makes templates texture fill scaling instead of tiling and does allow the usage of videos as texture. Reload for all connected clients is required for this to take effect if changed!",scope:"world",config:!0,default:!0,type:Boolean,onChange:()=>location.reload()}),game.settings.register("mess","change-placeables",{name:"Activate placeables changes.",hint:"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!",scope:"world",config:!0,default:!0,type:Boolean,onChange:()=>location.reload()}),e&&game.settings.registerMenu("mess","templateTexture",{name:"Mess template texture configurator for DnD abilities.",label:"Configure template textures",icon:"fas fa-mug-hot",type:s,restricted:!0}),game.settings.register("mess","templateTexture",{name:"Activate placeables changes.",hint:"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!",scope:"world",default:!0,type:Object})}static get defaultOptions(){return{...super.defaultOptions,template:"modules/mess/templates/settings.html",height:800,width:400}}constructor(e={},t){super(e,t),this.object=game.settings.get("mess","templateTexture")}getData(){let e=super.getData();return e.dmgTypes=CONFIG.DND5E.damageTypes,e.templateTypes=CONFIG.MeasuredTemplate.types,e}activateListeners(e){super.activateListeners(e)}_updateObject(e,t){game.settings.set("mess","templateTexture",mergeObject({},t))}}}}); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/scripts/index.js.map b/dist/scripts/index.js.map index 1a18562..dea7f3a 100644 --- a/dist/scripts/index.js.map +++ b/dist/scripts/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/scripts/index.js"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","installedModules","__webpack_require__","exports","module","l","e","promises","installedChunkData","promise","Promise","resolve","reject","onScriptComplete","script","document","createElement","charset","timeout","nc","setAttribute","src","p","jsonpScriptSrc","error","Error","event","onerror","onload","clearTimeout","chunk","errorType","type","realSrc","target","message","name","request","undefined","setTimeout","head","appendChild","all","m","c","d","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","oe","err","console","jsonpArray","window","oldJsonpFunction","slice","s","Hooks","on","async","game","settings","default","CONFIG","debug","mess","fromUuid","sheet","render","MessSettings","init","log"],"mappings":"aACE,SAASA,EAAqBC,GAQ7B,IAPA,IAMIC,EAAUC,EANVC,EAAWH,EAAK,GAChBI,EAAcJ,EAAK,GAKAK,EAAI,EAAGC,EAAW,GACpCD,EAAIF,EAASI,OAAQF,IACzBH,EAAUC,EAASE,GAChBG,OAAOC,UAAUC,eAAeC,KAAKC,EAAiBV,IAAYU,EAAgBV,IACpFI,EAASO,KAAKD,EAAgBV,GAAS,IAExCU,EAAgBV,GAAW,EAE5B,IAAID,KAAYG,EACZI,OAAOC,UAAUC,eAAeC,KAAKP,EAAaH,KACpDa,EAAQb,GAAYG,EAAYH,IAKlC,IAFGc,GAAqBA,EAAoBf,GAEtCM,EAASC,QACdD,EAASU,OAATV,GAOF,IAAIW,EAAmB,GAKnBL,EAAkB,CACrB,MAAS,GAWV,SAASM,EAAoBjB,GAG5B,GAAGgB,EAAiBhB,GACnB,OAAOgB,EAAiBhB,GAAUkB,QAGnC,IAAIC,EAASH,EAAiBhB,GAAY,CACzCI,EAAGJ,EACHoB,GAAG,EACHF,QAAS,IAUV,OANAL,EAAQb,GAAUU,KAAKS,EAAOD,QAASC,EAAQA,EAAOD,QAASD,GAG/DE,EAAOC,GAAI,EAGJD,EAAOD,QAKfD,EAAoBI,EAAI,SAAuBpB,GAC9C,IAAIqB,EAAW,GAKXC,EAAqBZ,EAAgBV,GACzC,GAA0B,IAAvBsB,EAGF,GAAGA,EACFD,EAASV,KAAKW,EAAmB,QAC3B,CAEN,IAAIC,EAAU,IAAIC,SAAQ,SAASC,EAASC,GAC3CJ,EAAqBZ,EAAgBV,GAAW,CAACyB,EAASC,MAE3DL,EAASV,KAAKW,EAAmB,GAAKC,GAGtC,IACII,EADAC,EAASC,SAASC,cAAc,UAGpCF,EAAOG,QAAU,QACjBH,EAAOI,QAAU,IACbhB,EAAoBiB,IACvBL,EAAOM,aAAa,QAASlB,EAAoBiB,IAElDL,EAAOO,IA1DV,SAAwBnC,GACvB,OAAOgB,EAAoBoB,EAAI,YAAc,CAAC,gBAAgB,gBAAgB,oBAAoB,oBAAoB,kBAAkB,kBAAkB,iBAAiB,iBAAiB,iBAAiB,iBAAiB,mBAAmB,mBAAmB,yBAAyB,0BAA0BpC,IAAUA,GAAW,IAAM,CAAC,EAAI,OAAO,gBAAgB,OAAO,oBAAoB,OAAO,kBAAkB,OAAO,iBAAiB,OAAO,iBAAiB,OAAO,mBAAmB,OAAO,yBAAyB,QAAQA,GAAW,MAyDhhBqC,CAAerC,GAG5B,IAAIsC,EAAQ,IAAIC,MAChBZ,EAAmB,SAAUa,GAE5BZ,EAAOa,QAAUb,EAAOc,OAAS,KACjCC,aAAaX,GACb,IAAIY,EAAQlC,EAAgBV,GAC5B,GAAa,IAAV4C,EAAa,CACf,GAAGA,EAAO,CACT,IAAIC,EAAYL,IAAyB,SAAfA,EAAMM,KAAkB,UAAYN,EAAMM,MAChEC,EAAUP,GAASA,EAAMQ,QAAUR,EAAMQ,OAAOb,IACpDG,EAAMW,QAAU,iBAAmBjD,EAAU,cAAgB6C,EAAY,KAAOE,EAAU,IAC1FT,EAAMY,KAAO,iBACbZ,EAAMQ,KAAOD,EACbP,EAAMa,QAAUJ,EAChBH,EAAM,GAAGN,GAEV5B,EAAgBV,QAAWoD,IAG7B,IAAIpB,EAAUqB,YAAW,WACxB1B,EAAiB,CAAEmB,KAAM,UAAWE,OAAQpB,MAC1C,MACHA,EAAOa,QAAUb,EAAOc,OAASf,EACjCE,SAASyB,KAAKC,YAAY3B,GAG5B,OAAOJ,QAAQgC,IAAInC,IAIpBL,EAAoByC,EAAI7C,EAGxBI,EAAoB0C,EAAI3C,EAGxBC,EAAoB2C,EAAI,SAAS1C,EAASiC,EAAMU,GAC3C5C,EAAoB6C,EAAE5C,EAASiC,IAClC5C,OAAOwD,eAAe7C,EAASiC,EAAM,CAAEa,YAAY,EAAMC,IAAKJ,KAKhE5C,EAAoBiD,EAAI,SAAShD,GACX,oBAAXiD,QAA0BA,OAAOC,aAC1C7D,OAAOwD,eAAe7C,EAASiD,OAAOC,YAAa,CAAEC,MAAO,WAE7D9D,OAAOwD,eAAe7C,EAAS,aAAc,CAAEmD,OAAO,KAQvDpD,EAAoBqD,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQpD,EAAoBoD,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKlE,OAAOmE,OAAO,MAGvB,GAFAzD,EAAoBiD,EAAEO,GACtBlE,OAAOwD,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOpD,EAAoB2C,EAAEa,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRxD,EAAoB4D,EAAI,SAAS1D,GAChC,IAAI0C,EAAS1C,GAAUA,EAAOqD,WAC7B,WAAwB,OAAOrD,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAF,EAAoB2C,EAAEC,EAAQ,IAAKA,GAC5BA,GAIR5C,EAAoB6C,EAAI,SAASgB,EAAQC,GAAY,OAAOxE,OAAOC,UAAUC,eAAeC,KAAKoE,EAAQC,IAGzG9D,EAAoBoB,EAAI,wBAGxBpB,EAAoB+D,GAAK,SAASC,GAA2B,MAApBC,QAAQ3C,MAAM0C,GAAYA,GAEnE,IAAIE,EAAaC,OAAqB,aAAIA,OAAqB,cAAK,GAChEC,EAAmBF,EAAWvE,KAAKgE,KAAKO,GAC5CA,EAAWvE,KAAOd,EAClBqF,EAAaA,EAAWG,QACxB,IAAI,IAAIlF,EAAI,EAAGA,EAAI+E,EAAW7E,OAAQF,IAAKN,EAAqBqF,EAAW/E,IAC3E,IAAIU,EAAsBuE,EAInBpE,EAAoBA,EAAoBsE,EAAI,0B;;;;8CClIrDC,MAAMC,GAAG,SAASC,iBAUjB,GATIC,KAAKC,SAAS3B,IAAI,OAAQ,2BACtB,gJAA6E4B,UACjFF,KAAKC,SAAS3B,IAAI,OAAQ,4BACtB,0IAA0E4B,UAC9EF,KAAKC,SAAS3B,IAAI,OAAQ,kCACtB,sKAAwF4B,UAC5FF,KAAKC,SAAS3B,IAAI,OAAQ,yBACtB,kIAAsE4B,UAE1EC,OAAOC,MAAMC,KAAM,QACDC,SAAS,2BACxBC,MAAMC,QAAO,OAIrBX,MAAMC,GAAG,QAAQC,iBAChBI,OAAOC,MAAMC,MAAO,SACb,0FAAyBI,aAAaC,OACzCP,OAAOC,MAAMC,OAChBd,QAAQoB,IAAIX,KAAKC,SAAS3B,IAAI,OAAQ,qBACtCiB,QAAQoB,IAAIX,KAAKC,SAAS3B,IAAI,OAAQ,mBACtCiB,QAAQoB,IAAIX,KAAKC,SAAS3B,IAAI,OAAQ,uBAEnC0B,KAAKC,SAAS3B,IAAI,OAAQ,4BACtB,8IAA4E4B,UAChFF,KAAKC,SAAS3B,IAAI,OAAQ,0BACtB,sIAAwE4B,UAC5EF,KAAKC,SAAS3B,IAAI,OAAQ,6BACtB,kJAA8E4B","file":"index.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t};\n\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t\"index\": 0\n \t};\n\n\n\n \t// script path function\n \tfunction jsonpScriptSrc(chunkId) {\n \t\treturn __webpack_require__.p + \"bundles/\" + ({\"add-scrolling\":\"add-scrolling\",\"change-placeables\":\"change-placeables\",\"draggable-items\":\"draggable-items\",\"item-sort-btns\":\"item-sort-btns\",\"modify-rolling\":\"modify-rolling\",\"modify-templates\":\"modify-templates\",\"prepared-spell-tracker\":\"prepared-spell-tracker\"}[chunkId]||chunkId) + \".\" + {\"0\":\"67b6\",\"add-scrolling\":\"1c30\",\"change-placeables\":\"9f20\",\"draggable-items\":\"a966\",\"item-sort-btns\":\"1907\",\"modify-rolling\":\"4bca\",\"modify-templates\":\"26bb\",\"prepared-spell-tracker\":\"dee3\"}[chunkId] + \".js\"\n \t}\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar promises = [];\n\n\n \t\t// JSONP chunk loading for javascript\n\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n \t\t\t// a Promise means \"currently loading\".\n \t\t\tif(installedChunkData) {\n \t\t\t\tpromises.push(installedChunkData[2]);\n \t\t\t} else {\n \t\t\t\t// setup Promise in chunk cache\n \t\t\t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t\t\t});\n \t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n \t\t\t\t// start chunk loading\n \t\t\t\tvar script = document.createElement('script');\n \t\t\t\tvar onScriptComplete;\n\n \t\t\t\tscript.charset = 'utf-8';\n \t\t\t\tscript.timeout = 120;\n \t\t\t\tif (__webpack_require__.nc) {\n \t\t\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t\t\t}\n \t\t\t\tscript.src = jsonpScriptSrc(chunkId);\n\n \t\t\t\t// create error before stack unwound to get useful stacktrace later\n \t\t\t\tvar error = new Error();\n \t\t\t\tonScriptComplete = function (event) {\n \t\t\t\t\t// avoid mem leaks in IE.\n \t\t\t\t\tscript.onerror = script.onload = null;\n \t\t\t\t\tclearTimeout(timeout);\n \t\t\t\t\tvar chunk = installedChunks[chunkId];\n \t\t\t\t\tif(chunk !== 0) {\n \t\t\t\t\t\tif(chunk) {\n \t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n \t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n \t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n \t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n \t\t\t\t\t\t\terror.type = errorType;\n \t\t\t\t\t\t\terror.request = realSrc;\n \t\t\t\t\t\t\tchunk[1](error);\n \t\t\t\t\t\t}\n \t\t\t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t\t\t}\n \t\t\t\t};\n \t\t\t\tvar timeout = setTimeout(function(){\n \t\t\t\t\tonScriptComplete({ type: 'timeout', target: script });\n \t\t\t\t}, 120000);\n \t\t\t\tscript.onerror = script.onload = onScriptComplete;\n \t\t\t\tdocument.head.appendChild(script);\n \t\t\t}\n \t\t}\n \t\treturn Promise.all(promises);\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"modules/mess/scripts/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n \tvar jsonpArray = window[\"webpackJsonp\"] = window[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/scripts/index.js\");\n","function registerSettings() {\n\tconst isDnD = game.system.id === 'dnd5e';\n\t\n\tgame.settings.register('mess', 'actor-item-sort', {\n\t\tname: \"Activate item sort button.\",\n\t\thint: \"Adds a button to actor sheets for sorting all items of that category alphabetically.\",\n\t\tscope: \"user\",\n\t\tconfig: isDnD,\n\t\tdefault: isDnD,\n\t\ttype: Boolean\n\t})\n\n\tgame.settings.register('mess', 'better-draggable', {\n\t\tname: \"Activate better drag'n'drop workflow.\",\n\t\tscope: \"user\",\n\t\tconfig: false,// Change if implemented\n\t\tdefault: isDnD,\n\t\ttype: Boolean\n\t})\n\n\tgame.settings.register('mess', 'prepared-spell-tracker', {\n\t\tname: \"Activate prepared spell tracker\",\n\t\thint: \"Adds a tracker to the spellbook tab, providing a way to track the allowed maximum of prepared spells.\",\n\t\tscope: \"user\",\n\t\tconfig: isDnD,\n\t\tdefault: isDnD,\n\t\ttype: Boolean\n\t})\n\n\tgame.settings.register('mess', 'add-scrolling', {\n\t\tname: \"Activating numerical field scrolling.\",\n\t\thint: \"Lets you in-/decrease numerical fields in the Actor sheet using the mouse wheel when focused.\",\n\t\tscope: \"user\",\n\t\tconfig: isDnD,\n\t\tdefault: isDnD,\n\t\ttype: Boolean\n\t});\n\n\tgame.settings.register('mess', 'modify-rolling', {\n\t\tname: \"Alternative Rolling.\",\n\t\thint: \"Changes the way rolling is displayed and executed for DnD5e. Reload for all connected clients is required for this to take effect if changed!\",\n\t\tscope: \"world\",\n\t\tconfig: isDnD,\n\t\tdefault: isDnD,\n\t\ttype: Boolean\n\t});\n\n\tgame.settings.register('mess', 'modify-templates', {\n\t\tname: \"Activate modified templates.\",\n\t\thint: \"Makes templates texture fill scaling instead of tiling and does allow the usage of videos as texture. Reload for all connected clients is required for this to take effect if changed!\",\n\t\tscope: \"world\",\n\t\tconfig: true,\n\t\tdefault: true,\n\t\ttype: Boolean\n\t});\n\t\n\tgame.settings.register('mess', 'change-placeables', {\n\t\tname: \"Activate placeables changes.\",\n\t\thint: \"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!\",\n\t\tscope: \"world\",\n\t\tconfig: true,\n\t\tdefault: true,\n\t\ttype: Boolean\n\t});\t\n}\n\n\nHooks.on('ready', async function() {\n\tif (game.settings.get('mess', 'actor-item-sort'))\n\t\t(await import(/* webpackChunkName: \"item-sort-btns\" */ './actor-item-sort-btn.js')).default();\n\tif (game.settings.get('mess', 'better-draggable'))\n\t\t(await import(/* webpackChunkName: \"draggable-items\" */ './draggable-items.js')).default();\n\tif (game.settings.get('mess', 'prepared-spell-tracker'))\n\t\t(await import(/* webpackChunkName: \"prepared-spell-tracker\" */ './prepared-spell-tracker.js')).default();\n\tif (game.settings.get('mess', 'add-scrolling'))\n\t\t(await import(/* webpackChunkName: \"add-scrolling\" */ './add-scrolling.js')).default();\n\t\n\tif (CONFIG.debug.mess) {\n\t\tconst actor = (await fromUuid('Actor.xV3LUAg05Pz5MFTS'));\n\t\tactor.sheet.render(true);\n\t}\n});\n\nHooks.on('init', async function() {\n\tCONFIG.debug.mess = false;\n\t(await import('./settings.js')).MessSettings.init();\n\tif (CONFIG.debug.mess) {\n\t\tconsole.log(game.settings.get('mess', 'modify-templates'))\n\t\tconsole.log(game.settings.get('mess', 'modify-rolling'))\n\t\tconsole.log(game.settings.get('mess', 'change-placeables'))\n\t}\n\tif (game.settings.get('mess', 'modify-templates'))\n\t\t(await import(/* webpackChunkName: \"modify-templates\" */ './modify-templates.js')).default();\n\tif (game.settings.get('mess', 'modify-rolling'))\n\t\t(await import(/* webpackChunkName: \"modify-rolling\" */ './modify-rolling.js')).default();\n\tif (game.settings.get('mess', 'change-placeables'))\n\t\t(await import(/* webpackChunkName: \"change-placeables\" */ './change-placeables.js')).default();\n});"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/scripts/actor-item-sort-btn.js","webpack:///./src/scripts/add-scrolling.js","webpack:///./src/scripts/change-placeables.js","webpack:///./src/scripts/index.js","webpack:///./src/scripts/modify-templates.js","webpack:///./src/scripts/prepared-spell-tracker.js","webpack:///./src/scripts/rolls/apply-dmg.js","webpack:///./src/scripts/rolls/controls.js","webpack:///./src/scripts/rolls/dice.js","webpack:///./src/scripts/rolls/index.js","webpack:///./src/scripts/rolls/modify-rolling.js","webpack:///./src/scripts/settings.js"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","installedModules","__webpack_require__","exports","module","l","e","promises","installedChunkData","promise","Promise","resolve","reject","onScriptComplete","script","document","createElement","charset","timeout","nc","setAttribute","src","p","jsonpScriptSrc","error","Error","event","onerror","onload","clearTimeout","chunk","errorType","type","realSrc","target","message","name","request","undefined","setTimeout","head","appendChild","all","m","c","d","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","oe","err","console","jsonpArray","window","oldJsonpFunction","slice","s","async","addAlphabeticalSorter","app","html","actor","_id","querySelectorAll","forEach","el","closest","dataset","tab","btn","innerHTML","classList","add","title","style","flex","margin","addEventListener","ev","lists","actorId","fromUuid","concatList","map","items","spells","flat","sort","a","b","siblings","sortUpdates","SortingHelpers","performIntegerSort","duplicate","sortBefore","updateData","u","update","updateEmbeddedEntity","sortItemListAlphabetically","prepend","itemSortBtn","Hooks","on","addScolling","preventDefault","stopPropagation","deltaY","currentTarget","Number","Math","max","$","change","onDragLeftMove","clones","destination","origin","originalEvent","canvas","_onDragCanvasPan","dx","x","dy","y","snap","previous","momentumThreshold","lambda","prev","prevMomentum","momentum","prevV","v","shiftKey","dest","_original","grid","getSnappedPosition","this","layer","options","gridPrecision","refresh","changePlaceables","PlaceableObject","_onDragLeftMove","game","settings","CONFIG","debug","mess","sheet","render","user","isGM","moduleVersion","version","register","default","String","scope","oldVersion","isNewerVersion","init","changeTemplateFill","newFun","MeasuredTemplate","toString","replace","Function","querySelector","dndTemplateSettings","system","id","itemHook","importedJS","import","AbilityTemplate","_originalFromItem","fromItem","item","template","path","getFlag","hasDamage","damage","parts","loadTexture","then","tex","texture","activatePreviewListeners","getTargets","isTokenInside","token","scene","templatePos","startX","width","startY","height","currGrid","shape","contains","tokens","getEmbeddedCollection","targets","updateTokenTargets","div","innerText","formField","inp","dtype","insertAdjacentHTML","button","_activateFilePicker","after","addPreparedSpellTracker","spellDc","tracker","border","spanPrep","preparedSpells","sep","val","isNaN","setFlag","parentNode","insertBefore","modifyApplyDmg","setProperty","canApply","li","controlled","find","condition","chatLogHook","advSelector","userId","templateData","advantage","normal","disadvantage","renderTemplate","arr","Array","from","currIdx","findIndex","remove","newSelected","set","toggle","controls","getElementById","childNodes","hit","dmg","getD20Modifier","getAdvantageSettings","rollD20","adv","nd","mods","halflingLucky","elvenAccuracy","i18n","localize","diceFormula","reliableTalent","unshift","d20Mod","Roll","join","roll","d20","total","tooltip","getTooltip","crit","fumble","chatData","CONST","CHAT_MESSAGE_TYPES","OTHER","content","speaker","alias","rollMode","includes","ChatMessage","getWhisperIDs","getToHitData","hasAttack","actorData","itemData","flags","dnd5e","rollData","getRollData","proficient","weaponCriticalThreshold","critical","parseInt","abilityMod","actorBonus","bonuses","actionType","attackBonus","attack","filterJoin","totalModifier","_safeEval","formula","_formula","situationalModifier","terms","rollToHit","disabled","card","messageId","messages","owner","isAuthor","ui","notifications","Item","entityClass","_getChatCardActor","getOwnedItem","itemId","span","idx","alter","replaceChild","getDmgData","spellLevel","level","versatile","splice","scaling","newDmgPart","lvl","details","_scaleCantripDamage","_scaleSpellDamage","part","DND5E","damageTypes","rollDmg","rolling","preCreateChatMessageHook","renderAttack","areaSkill","keys","areaTargetTypes","getProperty","size","img","attackData","toHit","dmgs","sceneId","autoroll","allowed","_handleResourceConsumption","isCard","isAttack","attackTemplateData","flavor","chatFlavor","autoRoll","toHitBtn","btns","actorSheetHook","abilityMods","off","abilityId","ability","label","abilities","mod","feats","remarkableAthlete","characterFlags","proficiency","ceil","attributes","prof","jackOfAllTrades","floor","checkBonus","format","abl","saveBonus","skills","skillId","skill","skl","bonus","chatListeners","onChatCardAction","_onChatCardToggleContent","onMouseEnterTarget","onMouseLeaveTarget","onDblClickTarget","action","placeTemplate","_onChatCardAction","getTargetToken","_onHoverIn","visible","_onHoverOut","pos","center","animatePan","tokenId","targetId","placeables","MessSettings","FormApplication","isDnD","hint","config","Boolean","onChange","location","reload","registerMenu","icon","restricted","super","defaultOptions","getData","dmgTypes","templateTypes","types","activateListeners","formData","mergeObject"],"mappings":"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","file":"index.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t};\n\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t\"index\": 0\n \t};\n\n\n\n \t// script path function\n \tfunction jsonpScriptSrc(chunkId) {\n \t\treturn __webpack_require__.p + \"bundles/\" + ({\"welcome-screen\":\"welcome-screen\"}[chunkId]||chunkId) + \".\" + {\"welcome-screen\":\"89ea\"}[chunkId] + \".js\"\n \t}\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar promises = [];\n\n\n \t\t// JSONP chunk loading for javascript\n\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n \t\t\t// a Promise means \"currently loading\".\n \t\t\tif(installedChunkData) {\n \t\t\t\tpromises.push(installedChunkData[2]);\n \t\t\t} else {\n \t\t\t\t// setup Promise in chunk cache\n \t\t\t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t\t\t});\n \t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n \t\t\t\t// start chunk loading\n \t\t\t\tvar script = document.createElement('script');\n \t\t\t\tvar onScriptComplete;\n\n \t\t\t\tscript.charset = 'utf-8';\n \t\t\t\tscript.timeout = 120;\n \t\t\t\tif (__webpack_require__.nc) {\n \t\t\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t\t\t}\n \t\t\t\tscript.src = jsonpScriptSrc(chunkId);\n\n \t\t\t\t// create error before stack unwound to get useful stacktrace later\n \t\t\t\tvar error = new Error();\n \t\t\t\tonScriptComplete = function (event) {\n \t\t\t\t\t// avoid mem leaks in IE.\n \t\t\t\t\tscript.onerror = script.onload = null;\n \t\t\t\t\tclearTimeout(timeout);\n \t\t\t\t\tvar chunk = installedChunks[chunkId];\n \t\t\t\t\tif(chunk !== 0) {\n \t\t\t\t\t\tif(chunk) {\n \t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n \t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n \t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n \t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n \t\t\t\t\t\t\terror.type = errorType;\n \t\t\t\t\t\t\terror.request = realSrc;\n \t\t\t\t\t\t\tchunk[1](error);\n \t\t\t\t\t\t}\n \t\t\t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t\t\t}\n \t\t\t\t};\n \t\t\t\tvar timeout = setTimeout(function(){\n \t\t\t\t\tonScriptComplete({ type: 'timeout', target: script });\n \t\t\t\t}, 120000);\n \t\t\t\tscript.onerror = script.onload = onScriptComplete;\n \t\t\t\tdocument.head.appendChild(script);\n \t\t\t}\n \t\t}\n \t\treturn Promise.all(promises);\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"modules/mess/scripts/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n \tvar jsonpArray = window[\"webpackJsonp\"] = window[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/scripts/index.js\");\n","async function sortItemListAlphabetically(lists, actorId) {\n\tconst actor = await fromUuid(`Actor.${actorId}`);\n\tlet concatList = lists.map(e => e.items || e.spells).flat();\n\tconcatList.sort(function (a, b) {\n\t\tif (a.name < b.name) return -1;\n\t\tif (a.name > b.name) return 1;\n\t\treturn 0;\n\t});\n\tlet siblings = [concatList.shift()];\n\tlet sortUpdates = [];\n\tfor (; concatList.length > 0; siblings.push(concatList.shift())) {\n\t\tsortUpdates = (SortingHelpers.performIntegerSort(concatList[0], {\n\t\t\ttarget: siblings[siblings.length-1], \n\t\t\tsiblings: duplicate(siblings),\n\t\t\tsortBefore: false}));\t\t\n\t}\n\tconst updateData = sortUpdates.map(u => {\n\t\tlet update = u.update;\n\t\tupdate._id = u.target._id;\n\t\treturn update;\n\t})\n\tactor.updateEmbeddedEntity('OwnedItem', updateData);\n}\n\nasync function addAlphabeticalSorter(app, html, data) {\n\tif (!data.actor._id) return;\n\tconst header = html.querySelectorAll('.filter-list');\n\n\theader.forEach(el => {\n\t\tconst type = el.closest('.tab').dataset.tab;\n\t\tconst btn = document.createElement('a');\n\t\tbtn.innerHTML = '';\n\t\tbtn.classList.add('mess-sort-btn');\n\t\tbtn.title = `Sort ${type} alphabetically.`;\n\t\tbtn.style.flex = 0;\n\t\tbtn.style.margin = \"0 5px 0 0\";\n\t\tbtn.addEventListener('click',\t(ev) => sortItemListAlphabetically(data[type], data.actor._id))\n\t\tel.prepend(btn);\n\t});\n}\n\nexport default async function itemSortBtn() {\n\tHooks.on('renderActorSheet', (app, html, data) => {\n\t\taddAlphabeticalSorter(app, html[0], data);\n\t});\n}","export default async function addScolling() {\n\tHooks.on('renderActorSheet', async function (app, html, data) {\n\t\thtml[0].querySelectorAll('input[data-dtype=\"Number\"], .item-uses input').forEach(el => {\n\t\t\tel.addEventListener('wheel', ev => {\n\t\t\t\tev.preventDefault();\n\t\t\t\tev.stopPropagation();\n\t\t\t\tif (ev.deltaY < 0)\n\t\t\t\t\tev.currentTarget.value = Number(ev.currentTarget.value) + 1;\n\t\t\t\tif (ev.deltaY > 0)\n\t\t\t\t\tev.currentTarget.value = Math.max(Number(ev.currentTarget.value) - 1, 0);\n\n\t\t\t\t$(ev.currentTarget).change()\n\t\t\t});\n\t\t})\n\t});\n}","function onDragLeftMove(event) {\n\tconst {clones, destination, origin, originalEvent} = event.data;\n\n\t// Pan the canvas if the drag event approaches the edge\n\tcanvas._onDragCanvasPan(originalEvent);\n\n\t// Determine dragged distance\n\tconst dx = destination.x - origin.x;\n\tconst dy = destination.y - origin.y;\n\n\tlet snap = false;\n\tif (event.data.previous) {\n\t\t// Interesting would be here how this all behaves for different monitor sizes/performances and when Atro possibly introduces more adaptive rates for mosue movement. \n\t\t// Why? Because currently i just set the timedifferences between function calls to 1. So for 60Hz it behaves same as 30Hz although for 30Hz the distance travelled could be bigger.\n\t\t// All this because i don't want divisions in here for the moment....\n\t\tconst momentumThreshold = 30;\n\t\t// smaller lambda means less \"memory\" => current momentum does have more impact\n\t\t// => more jumping around between snapping and not\n\t\t// but higher means more waiting/slow movement time for it to snap\n\t\tconst lambda = 0.8;\n\t\tconst prev = event.data.previous;\n\t\tconst prevMomentum = event.data.momentum || 0;\n\t\tconst prevV = event.data.v || {x: 0, y: 0};\n\t\tconst v = {\n\t\t\tx: destination.x - prev.x,\n\t\t\ty: destination.y - prev.y\n\t\t}\n\t\tconst momentum = {\n\t\t\tx: v.x - prevV.x,\n\t\t\ty: v.y - prevV.y\n\t\t};\n\n\t\tevent.data.momentum = (momentum.x * momentum.x + momentum.y * momentum.y) + prevMomentum * lambda;\n\t\tsnap = !event.shiftKey && event.data.momentum < momentumThreshold;\n\t}\n\n\tevent.data.previous = destination;\n\n\t// Update the position of each clone\n\tfor ( let c of clones || [] ) {\n\t\tlet dest = {x: c._original.data.x + dx, y: c._original.data.y + dy};\n\t\tif ( snap ) {\n\t\t\tdest = canvas.grid.getSnappedPosition(dest.x, dest.y, this.layer.options.gridPrecision);\n\t\t}\n\t\tc.data.x = dest.x;\n\t\tc.data.y = dest.y;\n\t\tc.refresh();\n\t}\n}\n\nexport function changePlaceables() {\n\tPlaceableObject.prototype._onDragLeftMove = onDragLeftMove;\n\t// Change got bigger than expected, so.. complete swap it is\n}","import { rolling } from './rolls';\nimport { MessSettings } from './settings.js';\nimport {dndTemplateSettings, changeTemplateFill } from './modify-templates.js';\nimport { changePlaceables } from './change-placeables.js';\nimport itemSortBtn from './actor-item-sort-btn.js';\nimport preparedSpellTracker from './prepared-spell-tracker.js';\nimport addScrolling from './add-scrolling.js';\n\nHooks.on('ready', async function() {\n\tif (game.settings.get('mess', 'actor-item-sort'))\n\t\titemSortBtn();\n\tif (game.settings.get('mess', 'prepared-spell-tracker'))\n\t\tpreparedSpellTracker();\n\tif (game.settings.get('mess', 'add-scrolling'))\n\t\taddScrolling();\n\t\n\tif (CONFIG.debug.mess) {\n\t\tconst actor = (await fromUuid('Actor.xV3LUAg05Pz5MFTS'));\n\t\tactor.sheet.render(true);\n\t}\n\n\tif (!game.user.isGM)\n\treturn;\n\t// Edit next line to match module.\n\tconst module = game.modules.get(\"mess\");\n\tconst title = module.data.title;\n\tconst moduleVersion = module.data.version;\n\tgame.settings.register(title, 'version', {\n\t\tname: `${title} Version`,\n\t\tdefault: \"0.0.0\",\n\t\ttype: String,\n\t\tscope: 'world',\n\t});\n\tconst oldVersion = game.settings.get(title, \"version\");\n\n\tif (!isNewerVersion(moduleVersion, oldVersion))\n\t\treturn;\n\n\t(await import(\n\t\t\t\t\t\t\t\t/* webpackChunkName: \"welcome-screen\" */\n\t\t\t\t\t\t\t\t'./welcome-screen.js'\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t).default();\n});\n\nHooks.on('init', function() {\n\tCONFIG.debug.mess = false;\n\tMessSettings.init();\n\n\trolling();\n\n\tif (game.settings.get('mess', 'modify-templates')) {\n\t\tdndTemplateSettings();\n\t\tchangeTemplateFill();\n\t}\n\tif (game.settings.get('mess', 'change-placeables'))\n\t\tchangePlaceables();\n});\n\n\n\n\n/** Think of something here to move that into modify rolling\n * MAYBE i finally have to give up on the thought of dynamic loading of everything....\n * maybe create initial file for everything, and then dynamically load content there\n * \n */\n// Hooks.on('setup', () => {\n// \tif (game.settings.get('mess', 'modify-rolling'))\n// \t\tHooks.on('getChatLogEntryContext', (html, options) => {\n// \t\t\toptions.forEach(e => e.condition = false)\n// \t\t\treturn [];\n// \t\t})\n// })","export function changeTemplateFill() {\n\n // #MonkeyPatchingFTW\n // better than stealing the code, replacing one line and then release it under a/the wrong license..\n // Disadvantage: could need more fixing on updates. At least i didn#t make it line based like Kakaroto.. :P\n let oldFun = MeasuredTemplate.prototype.refresh.toString();\n \n let newFun = oldFun.replace(/this\\.template\\.beginTextureFill\\(\\{[\\s\\S]*\\}\\)\\;/, `{\n let mat = PIXI.Matrix.IDENTITY;\n // rectangle\n if (this.shape.width && this.shape.height)\n mat.scale(this.shape.width / this.texture.width, this.shape.height / this.texture.height);\n else if (this.shape.radius) {\n mat.scale(this.shape.radius * 2 / this.texture.height, this.shape.radius * 2 / this.texture.width)\n // Circle center is texture start...\n mat.translate(-this.shape.radius, -this.shape.radius);\n } else if (this.data.t === \"ray\") {\n const d = canvas.dimensions,\n height = this.data.width * d.size / d.distance,\n width = this.data.distance * d.size / d.distance;\n mat.scale(width / this.texture.width, height / this.texture.height);\n mat.translate(0, -height * 0.5);\n\n mat.rotate(toRadians(this.data.direction));\n } else {// cone\n const d = canvas.dimensions;\n \n // Extract and prepare data\n let {direction, distance, angle} = this.data;\n distance *= (d.size / d.distance);\n direction = toRadians(direction);\n const width = this.data.distance * d.size / d.distance;\n\n const angles = [(angle/-2), (angle/2)];\n distance = distance * Math.cos(toRadians(angle/2));\n \n // Get the cone shape as a polygon\n const rays = angles.map(a => Ray.fromAngle(0, 0, direction + toRadians(a), distance+1));\n const height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x)\n + (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y));\n mat.scale(width / this.texture.width, height / this.texture.height);\n mat.translate(0, -height/2)\n mat.rotate(toRadians(this.data.direction));\n }\n this.template.beginTextureFill({\n texture: this.texture,\n matrix: mat,\n alpha: 0.8\n });\n // move into draw or so\n const source = getProperty(this.texture, \"baseTexture.resource.source\")\n if ( source && (source.tagName === \"VIDEO\") ) {\n source.loop = true;\n source.muted = true;\n game.video.play(source);\n }\n }`);\n MeasuredTemplate.prototype.refresh = Function(`\"use strict\"; return ( function ${newFun} )`)();\n\n Hooks.on('renderMeasuredTemplateConfig', (app, html, data) => {\n html[0].querySelector('.file-picker').dataset.type = 'imagevideo'\n });\n}\n\nexport async function dndTemplateSettings() {\n if (game.system.id !== 'dnd5e') return;\n\n Hooks.on('renderItemSheet', itemHook);\n \n const importedJS = (await import(/* webpackIgnore: true */ '/systems/dnd5e/module/pixi/ability-template.js'))\n\tconst AbilityTemplate = importedJS.default || importedJS.AbilityTemplate;\n\n\t\n\tconst _originalFromItem = AbilityTemplate.fromItem;\n\tAbilityTemplate.fromItem = function(item) {\n\t\tconst template = _originalFromItem.bind(this)(item);\n\t\t\n\t\t// generate a texture based on the items dmg type, ...\n\t\t// Add settings to define custom templates for stuff.\n\t\tlet path = item.getFlag('mess', 'templateTexture');\n\t\tif (!path && item.hasDamage) {\n\t\t\tconst settings = game.settings.get('mess', 'templateTexture') || {};\n\t\t\tpath = settings[item.data.data.damage.parts[0][1]] || {};\n\t\t\tpath = path[template.data.t];\n\t\t}\n\t\tif (path)\n\t\t\tloadTexture(path).then(tex => {\n\t\t\t\ttemplate.texture = tex;\n\t\t\t\ttemplate.data.texture = path;\n\t\t\t\ttemplate.refresh();\n\t\t\t})\n\t\ttemplate.item = item;\n\t\treturn template;\n\t}\n\n\t// rather ugly, maybe find a better way at some point :shrug:\n\tconst origPrevListeners = AbilityTemplate.prototype.activatePreviewListeners.toString();\n\tconst newFun = origPrevListeners.replace(/this\\.refresh\\(\\)\\;/, \n\t\t\t\t// get targets\n\t\t\t\t\t`this.refresh();\n\t\t\t\t\tthis.getTargets(this);\n\t\t\t\t`);\n\n\tAbilityTemplate.prototype.getTargets = getTargets;\n\tAbilityTemplate.prototype.isTokenInside = isTokenInside;\n\n\tAbilityTemplate.prototype.activatePreviewListeners = Function(`\"use strict\"; return ( function ${newFun} )`)();\n}\n\n\nfunction isTokenInside(token) {\n\tconst grid = canvas.scene.data.grid,\n\t\t\t\ttemplatePos = {x: this.data.x, y: this.data.y};\n\t// Check for center of each square the token uses.\n\t// e.g. for large tokens all 4 squares\n\tconst startX = token.width >= 1 ? 0.5 : token.width / 2;\n\tconst startY = token.height >= 1 ? 0.5 : token.height / 2;\n\tfor (let x = startX; x < token.width; x++) {\n\t\tfor (let y = startY; y < token.height; y++) {\n\t\t\tconst currGrid = {\n\t\t\t\tx: token.x + x * grid - templatePos.x,\n\t\t\t\ty: token.y + y * grid - templatePos.y\n\t\t\t};\n\t\t\tconst contains = this.shape.contains(currGrid.x, currGrid.y);\n\t\t\tif (contains) return true;\n\t\t}\n\t}\n\treturn false;\n}\n\nfunction getTargets() {\n\tconst tokens = canvas.scene.getEmbeddedCollection('Token');\n\tlet targets = [];\n\t\n\tfor (const token of tokens)\n\t\tif (this.isTokenInside(token)) { targets.push(token._id); }\n\tgame.user.updateTokenTargets(targets);\n}\n\nasync function itemHook(app, html) {\n\tconst div = document.createElement('div');\n\tdiv.classList.add('form-group');\n\tdiv.appendChild(document.createElement('label')).innerText = 'Template Texture';\n\tconst formField = div.appendChild(document.createElement('div'));\n\tformField.classList.add('form-fields');\n\tconst inp = formField.appendChild(document.createElement('input'));\n\tinp.dataset.dtype = 'String';\n\tinp.type = 'text';\n\tinp.name = 'flags.mess.templateTexture';\n\tinp.value = app.object.getFlag('mess', 'templateTexture') || \"\";\n\n\tformField.insertAdjacentHTML('beforeend', `\n\t\t\n\t`);\n\tconst button = formField.querySelector('button');\n\tbutton.style.flex = '0';\n app._activateFilePicker(button);\n const target = html[0].querySelector('[name=\"data.target.units\"]');\n if (target)\n\t target.closest('.form-group').after(div);\n}","export default async function addPreparedSpellTracker() {\n\tHooks.on('renderActorSheet', async function (app, html, data) {\n\t\tconst actor = await fromUuid(`Actor.${data.actor._id}`);\n\t\tconst spellDc = html[0].querySelector('.spell-dc');\n\t\tif (!spellDc) return;\n\t\tlet tracker = document.createElement('div');\n\t\ttracker.classList.add('spell-slots');\n\t\ttracker.style.flex = '1';\n\t\ttracker.style.border = 'none';\n\t\tconst spanPrep = tracker.appendChild(document.createElement('span'));\n\t\tspanPrep.innerText = `prepared spells: ${data.preparedSpells}`;\n\t\tspanPrep.classList.add('spell-max');\n\n\t\tconst sep = tracker.appendChild(document.createElement('span'));\n\t\tsep.innerText = ' / ';\n\t\tsep.classList.add('sep');\n\n\t\tconst max = tracker.appendChild(document.createElement('input'));\n\t\tmax.type = 'text';\n\t\tmax.value = actor.getFlag('mess', 'maxPreparedSpells') || 0;\n\t\tmax.addEventListener('change', async function(ev) {\n\t\t\tev.preventDefault();\n\t\t\tev.stopPropagation();\n\t\t\tconst val = Number(ev.currentTarget.value);\n\t\t\tif (isNaN(val)) {\n\t\t\t\tev.currentTarget.value = actor.getFlag('mess', 'maxPreparedSpells') || 0;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tactor.setFlag('mess', 'maxPreparedSpells', val);\n\t\t\treturn false;\n\t\t});\n\t\tspellDc.parentNode.insertBefore(tracker, spellDc);\n\t});\n}","export default function modifyApplyDmg() {\n\tHooks.on('getChatLogEntryContext', function (html, options) {\n\t\tsetProperty(game, 'mess.chatLogEntryContextOptions', options);\n\t\t\n\t\t// Modify existing applies to only work on default rolls and not mess-rolls\n\t\tconst canApply = li => canvas.tokens.controlled.length \n\t\t\t\t\t\t\t\t\t\t\t\t&& li.find(\".dice-roll .dice-result\").length;\n\t\tfor (let i = 1; i < options.length; i++) \n\t\t\toptions[i].condition = canApply;\n\t});\n}","export default function() {\n\n\tHooks.on('renderChatLog', chatLogHook);\n\n\tregisterSettings();\n};\n\nfunction registerSettings() {\n\tgame.settings.register('mess', `${game.userId}.adv-selector`, {\n\t\tname: 'Mess - Advantage Selector',\n\t\tdefault: 'normal',\n\t\ttype: String,\n\t\tscope: 'user'\n\t});\n\tgame.settings.register('mess', `${game.userId}.autoroll-selector`, {\n\t\tname: 'Mess - Autoroll Selector',\n\t\tdefault: {hit: false, dmg: false},\n\t\ttype: Object,\n\t\tscope: 'user'\n\t});\n}\n\nasync function chatLogHook(app, html, data) {\n\tconst div = document.createElement('div');\n\tdiv.classList.add('mess-roll-control');\n\n\tconst advSelector = game.settings.get('mess', `${game.userId}.adv-selector`);\n\tconst autoRollSelector = game.settings.get('mess', `${game.userId}.autoroll-selector`);\n\tconst templateData = {\n\t\tadvantage: advSelector === 'advantage',\n\t\tnormal: advSelector === 'normal',\n\t\tdisadvantage: advSelector === 'disadvantage',\n\t\t...autoRollSelector\n\t}\n\n\tdiv.insertAdjacentHTML('afterbegin', await renderTemplate('modules/mess/templates/roll-control.html', templateData));\n\n\tdiv.querySelectorAll('.mess-adv-selector a').forEach(e => {\n\t\te.addEventListener('click', async function(ev) {\n\t\t\tev.preventDefault();\n\t\t\tev.stopPropagation();\n\t\t\tconst arr = Array.from(ev.currentTarget.parentNode.querySelectorAll('a'));\n\t\t\tconst currIdx = arr.findIndex(e => e.classList.contains('mess-selected'));\n\t\t\tarr[currIdx].classList.remove('mess-selected');\n\t\t\tconst newSelected = arr[(currIdx + 1) % arr.length];\n\t\t\tnewSelected.classList.add('mess-selected');\n\t\t\tgame.settings.set('mess', `${game.userId}.adv-selector`, newSelected.name);\n\t\t});\n\t\t// Toggle in the opposite direction with right click\n\t\te.addEventListener('contextmenu', async function(ev) {\n\t\t\tev.preventDefault();\n\t\t\tev.stopPropagation();\n\t\t\tconst arr = Array.from(ev.currentTarget.parentNode.querySelectorAll('a'));\n\t\t\tconst currIdx = arr.findIndex(e => e.classList.contains('mess-selected'));\n\t\t\tarr[currIdx].classList.remove('mess-selected');\n\t\t\tconst newSelected = arr[(currIdx + arr.length - 1) % arr.length]; // add length cause javascripts modulo is strange\n\t\t\tnewSelected.classList.add('mess-selected');\n\t\t\tgame.settings.set('mess', `${game.userId}.adv-selector`, newSelected.name);\n\t\t})\n\t});\n\tdiv.querySelectorAll('.mess-autoroll-selector a') .forEach(e => {\n\t\te.addEventListener('click', async function(ev) {\n\t\t\tev.preventDefault();\n\t\t\tev.stopPropagation();\n\n\t\t\t\n\t\t\tev.currentTarget.classList.toggle('mess-selected');\n\t\t\tlet data = game.settings.get('mess', `${game.userId}.autoroll-selector`);\n\t\t\tdata[ev.currentTarget.name] = ev.currentTarget.classList.contains('mess-selected');\n\t\t\tgame.settings.set('mess', `${game.userId}.autoroll-selector`, data);\n\t\t})\n\t});\n\n\tconst controls = document.getElementById('chat-controls');\n\tcontrols.insertBefore(div, controls.childNodes[0]);\n}","function getD20Modifier() {\n\treturn document.getElementById('mess-roll-mod').value;\n}\n\nfunction getAdvantageSettings() {\n\treturn game.settings.get('mess', `${game.userId}.adv-selector`);\n}\n\nexport async function rollD20(data) {\n\tlet adv = getAdvantageSettings();\n\t// Determine the d20 roll and modifiers\n\tlet nd = 1;\n\tlet mods = data.halflingLucky ? \"r=1\" : \"\";\n\n\t// Handle advantage\n\tif ( adv === \"advantage\" ) {\n\t\tnd = data.elvenAccuracy ? 3 : 2;\n\t\tmods += \"kh\";\n\t\tdata.title += ` (${game.i18n.localize(\"DND5E.Advantage\")})`;\n\t}\n\n\t// Handle disadvantage\n\telse if ( adv === \"disadvantage\" ) {\n\t\tnd = 2;\n\t\tmods += \"kl\";\n\t\tdata.title += ` (${game.i18n.localize(\"DND5E.Disadvantage\")})`;\n\t}\n\n\t// Include the d20 roll\n\tlet diceFormula = `${nd}d20${mods}`;\n\tif (data.reliableTalent) diceFormula = `{${nd}d20${mods},10}kh`;\n\tdata.parts.unshift(diceFormula);\n\n\tconst d20Mod = getD20Modifier();\n\tif (d20Mod)\n\t\tdata.parts.push(d20Mod);\n\t\n\tlet r = new Roll(data.parts.join('+'), data);\n\tr.roll();\n\tconst d20 = r.parts[0].total;\n\tlet templateData = {...data, \n\t\ttooltip: await r.getTooltip(),\n\t\troll: r,\n\t\tcrit: d20 >= 20,\n\t\tfumble: d20 <= 1\n\t}\n\n\tconst template = await renderTemplate('modules/mess/templates/roll-card.html', templateData);\n\n\tlet chatData = {\n\t\tuser: game.user._id,\n\t\ttype: CONST.CHAT_MESSAGE_TYPES.OTHER,\n\t\tcontent: template,\n\t\tspeaker: {\n\t\t\tactor: this,\n\t\t\talias: this.name\n\t\t}\n\t};\n\tlet rollMode = game.settings.get(\"core\", \"rollMode\");\n\tif ( [\"gmroll\", \"blindroll\"].includes(rollMode) ) chatData[\"whisper\"] = ChatMessage.getWhisperIDs(\"GM\");\n\tif ( rollMode === \"blindroll\" ) chatData[\"blind\"] = true;\n\n\tChatMessage.create(chatData);\n}\n\n/**\n * Extracts the data needed for an attack roll.\n * @param {ActorEntity} actor\n * @param {ItemEntity} item \n */\nexport async function getToHitData({actor, item}) {\n\tif (!item.hasAttack) return null;\n\tconst actorData = actor.data.data;\n\tconst itemData = item.data.data;\n\tconst flags = actor.data.flags.dnd5e || {};\n\t\n\tlet rollData = item.getRollData();\n\n\t// Define Roll bonuses\n\tconst parts = [`@mod`];\n\tif ( (item.data.type !== \"weapon\") || itemData.proficient ) {\n\t\tparts.push(\"@prof\");\n\t}\n\trollData.parts = parts;\n\n\t// Expanded weapon critical threshold\n\tif (( item.data.type === \"weapon\" ) && flags.weaponCriticalThreshold) {\n\t\trollData.critical = parseInt(flags.weaponCriticalThreshold);\n\t}\n\n\t// Elven Accuracy\n\tif ( [\"weapon\", \"spell\"].includes(item.data.type) ) {\n\t\tif (flags.elvenAccuracy && [\"dex\", \"int\", \"wis\", \"cha\"].includes(item.abilityMod)) {\n\t\t\trollData.elvenAccuracy = true;\n\t\t}\n\t}\n\n\t// Apply Halfling Lucky\n\tif ( flags.halflingLucky ) rollData.halflingLucky = true;\n\n\t// Attack Bonus\n\tconst actorBonus = actorData.bonuses[itemData.actionType] || {};\n\tif ( itemData.attackBonus || actorBonus.attack ) {\n\t\t// parts.push(\"@atk\");\n\t\trollData[\"atk\"] = [itemData.attackBonus, actorBonus.attack].filterJoin(\" + \");\n\t\tif (!isNaN(Number(rollData[\"atk\"]))) {\n\t\t\tparts.push(\"@atk\");\n\t\t}\n\t}\n\n\tlet roll = new Roll(rollData.parts.join('+'), rollData);\n\trollData.totalModifier = roll._safeEval(roll.formula);\n\trollData.totalModifier = rollData.totalModifier >= 0 ? '+' + rollData.totalModifier : rollData.totalModifier;\n\tif (rollData[\"atk\"] && !roll._formula.includes('@atk')) {\n\t\trollData.parts.push(\"@atk\");\n\t\troll = new Roll(rollData.parts.join('+'), rollData);\n\t\trollData.totalModifier += `+${rollData[\"atk\"]}`;\n\t}\n\n\tconst situationalModifier = getD20Modifier();\n\tif (situationalModifier) {\n\t\trollData.parts.push(situationalModifier);\n\t\troll = new Roll(rollData.parts.join('+'), rollData);\n\t\trollData.totalModifier += `+${situationalModifier}`;\n\t}\n\trollData.formula = roll.formula;\n\trollData.terms = roll._formula;\n\treturn rollData;\n}\n\n/**\n * Rolls the to hit roll and updates the html target. Also updates the message if found.\n * @param {Click Event} ev event targetting the button which initiated a real or virtual click event\n */\nexport async function rollToHit(ev) {\n\t// Extract card data\n\tconst button = ev.currentTarget;\n\tbutton.disabled = true;\n\tconst card = button.closest(\".chat-card\");\n\tconst messageId = card.closest(\".message\").dataset.messageId;\n\t// Check if user owns chat message, else return\n\tif (messageId) {\n\t\tconst message = game.messages.get(messageId);\n\t\tif (!(message.owner || message.isAuthor)) {\n\t\t\tui.notifications.error('You do not own the permissions to make that roll!');\n\t\t\treturn;\n\t\t}\n\t}\n\t// Get the Actor from a synthetic Token\n\tconst actor = CONFIG.Item.entityClass._getChatCardActor(card);\n\tif (!actor.owner) return false;\n\n\t// Get the Item\n\tconst item = actor.getOwnedItem(card.dataset.itemId);\n\tif ( !item ) {\n\t\treturn ui.notifications.error(`The requested item ${card.dataset.itemId} no longer exists on Actor ${actor.name}`)\n\t}\n\n\tlet rollData = await getToHitData({actor, item});\n\tlet adv = getAdvantageSettings();\n\t// Determine the d20 roll and modifiers\n\tlet nd = 1;\n\tlet mods = rollData.halflingLucky ? \"r=1\" : \"\";\n\n\t// Handle advantage\n\tif ( adv === \"advantage\" ) {\n\t\tnd = rollData.elvenAccuracy ? 3 : 2;\n\t\tmods += \"kh\";\n\t}\n\n\t// Handle disadvantage\n\telse if ( adv === \"disadvantage\" ) {\n\t\tnd = 2;\n\t\tmods += \"kl\";\n\t}\n\n\t// Include the d20 roll\n\trollData.parts.unshift(`${nd}d20${mods}`);\n\t\n\tlet r = new Roll(rollData.parts.join('+'), rollData);\n\tr.roll();\n\tlet div = document.createElement('div');\n\tdiv.title = `${rollData.parts[0]}+${rollData.terms} = ${r.formula} = ${r.total}. Click to see rolls.`;\n\tdiv.classList.add('dice-roll');\n\tdiv.classList.add('mess-dice-result');\n\tconst span = div.appendChild(document.createElement('span'));\n\tspan.innerText = r.total;\n\tdiv.insertAdjacentHTML('beforeend', await r.getTooltip());\n\tconst tooltip = div.childNodes[1];\n\ttooltip.classList.add('hidden');\n\tconst crit = rollData.critical || 20;\n\tconst fumble = rollData.fumble || 1;\n\n\tconst d20 = r.parts[0].total;\n\tif (d20 >= crit) {\n\t\tspan.classList.add('crit');\n\t\tcard.querySelector('.mess-chat-dmg .mess-chat-roll-type').innerHTML += ' - Crit!'\n\t\tcard.querySelectorAll('.mess-button-dmg').forEach((e, idx) => {\n\t\t\tconst formula = e.dataset.formula;\n\t\t\tconst r = new Roll(formula);\n\t\t\tr.alter(0, 2);\n\t\t\te.innerHTML = ` ${r.formula}`\n\t\t\te.dataset.formula = r.formula;\n\t\t});\n\t}\n\tif (d20 <= fumble)\n\t\tspan.classList.add('fumble');\n\n\tev.currentTarget.parentNode.replaceChild(div, ev.currentTarget);\n\tif (messageId) {\n\t\tconst message = game.messages.get(messageId);\n\t\tmessage.update({content: card.parentNode.innerHTML});\n\t}\n}\n\nexport async function getDmgData({actor, item, spellLevel = null}) {\n\tif (!item.hasDamage) return null;\n\tconst actorData = actor.data.data;\n\tconst itemData = item.data.data;\n\tlet rollData = item.getRollData();\n\t\n\tif ( spellLevel ) rollData.item.level = spellLevel;\n\n\trollData.parts = duplicate(itemData.damage.parts);\n\tif (itemData.damage.versatile) \n\t\trollData.parts.splice(1, 0, [itemData.damage.versatile, \"versatile\"]);\n\t\n\tif (item.data.type === 'spell') {\n\t\tif (itemData.scaling.mode === 'cantrip') {\n\t\t\tlet newDmgPart = [rollData.parts[0][0]];\n\t\t\tconst lvl = actor.data.type === 'character' ? actorData.details.level : actorData.details.spellLevel;\n\t\t\titem._scaleCantripDamage(newDmgPart, lvl, itemData.scaling.formula);\n\t\t\trollData.parts[0][0] = newDmgPart[0];\n\t\t} else if (spellLevel && (itemData.scaling.mode === 'level') && itemData.scaling.formula ) {\n\t\t\tlet newDmgPart = [];\n\t\t\titem._scaleSpellDamage(newDmgPart, itemData.level, spellLevel, itemData.scaling.formula)\n\t\t\tif (newDmgPart.length > 0) {\n\t\t\t\tnewDmgPart.push('upcast dice');\n\t\t\t\trollData.parts.push(newDmgPart);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tconst actorBonus = actorData.bonuses[itemData.actionType] || {};\n\tif (actorBonus.damage && parseInt(actorBonus.damage ) !== 0) {\n\t\tparts[0][0] += \"+@dmg\";\n\t\trollData[\"dmg\"] = actorBonus.damage;\n\t}\n\n\tfor (let part of rollData.parts) {\n\t\tlet roll = new Roll(part[0], rollData);\n\t\tconst dmgType = CONFIG.DND5E.damageTypes[part[1]];\n\t\tif (dmgType)\n\t\t\tpart[1] = game.i18n.localize('DND5E.Damage' + CONFIG.DND5E.damageTypes[part[1]]);\n\t\telse if (part[1] === 'versatile')\n\t\t\tpart[1] = game.i18n.localize('DND5E.Versatile');\n\t\tpart.push(roll.formula);\n\t}\n\n\treturn rollData;\n}\n\n/**\n * Rolls the dmg dice listed. If the event points towards an existing message the message will get updated\n * @param {Event} ev \n */\nexport async function rollDmg(ev) {\n\t// Extract card data\n\tconst button = ev.currentTarget;\n\tbutton.disabled = true;\n\tconst card = button.closest(\".chat-card\");\n\tconst messageId = card.closest(\".message\").dataset.messageId;\n\n\t// Check if user owns chat message, else return\n\tif (messageId) {\n\t\tconst message = game.messages.get(messageId);\n\t\tif (!(message.owner || message.isAuthor)) {\n\t\t\tui.notifications.error('You do not own the permissions to make that roll!');\n\t\t\treturn;\n\t\t}\n\t}\n\tconst formula = button.dataset.formula;\n\n\tlet r = new Roll(formula);\n\tr.roll();\n\tlet div = document.createElement('div');\n\tdiv.title = `${button.dataset.terms} = ${r.formula} = ${r.total}. Click to see rolls.`;\n\tdiv.classList.add('dice-roll');\n\tdiv.classList.add('mess-dice-result');\n\tconst span = div.appendChild(document.createElement('span'));\n\tspan.innerText = r.total;\n\tdiv.insertAdjacentHTML('beforeend', await r.getTooltip());\n\tconst tooltip = div.childNodes[1];\n\ttooltip.classList.add('hidden');\n\n\tev.currentTarget.parentNode.replaceChild(div, ev.currentTarget);\n\n\tif (messageId) {\n\t\tconst message = game.messages.get(messageId);\n\t\tmessage.update({content: card.parentNode.innerHTML});\n\t}\n}","import rollConrolls from './controls.js';\nimport applyDmg from './apply-dmg.js';\nimport modifyRolling from './modify-rolling.js';\n\nexport function rolling() {\n\t// Don't do this stuff if the settings are disabled.\n\tif (!game.settings.get('mess', 'modify-rolling'))\n\t\treturn;\n\trollConrolls();\n\tapplyDmg();\n\tmodifyRolling()\n}","import {rollD20, getToHitData, rollToHit, getDmgData, rollDmg} from './dice.js';\n\n\nexport default function() {\n\tsetupHooks();\n}\n\n/**\n * Initializes all the hoohks!\n */\nfunction setupHooks() {\n\tHooks.on('preCreateChatMessage', preCreateChatMessageHook);\n\tHooks.on('renderActorSheet', actorSheetHook);\n\n\t// Bind my own chatListeners to the item class and execute them.\n\tHooks.on('ready', chatListeners.bind(CONFIG.Item.entityClass));\n}\n\n/**\n * Makes sure one attack is rolled for every chat card that has a dmg or attack button.\n * @param {Object} data chat message data\n */\nasync function preCreateChatMessageHook(data) {\n\tconst div = document.createElement('div');\n\tdiv.insertAdjacentHTML('afterbegin', data.content);\n\tlet btn = div.querySelector('button[data-action=\"attack\"]');\n\tif (!btn)\n\t\tbtn = div.querySelector('button[data-action=\"damage\"]');\n\t\n\tif (btn)\n\t\trenderAttack({currentTarget: btn});\n}\n\n/**\n * Renders an attack chat card\n * @param {Click Event} ev pointing towards the card that is supposed to initiate the event.\n */\nasync function renderAttack(ev) {\n\tif (ev.type === 'click') {\n\t\tev.preventDefault();\n\t\tev.stopPropagation();\n\t}\n\n\t// Extract card data\n\tconst button = ev.currentTarget;\n\tbutton.disabled = true;\n\tconst card = button.closest(\".chat-card\");\n\n\t// Get the Actor from a synthetic Token\n\tconst actor = CONFIG.Item.entityClass._getChatCardActor(card);\n\n\tif ( !actor || !actor.owner) return;\n\n\t// Get the Item\n\tconst item = actor.getOwnedItem(card.dataset.itemId);\n\tif ( !item ) {\n\t\treturn ui.notifications.error(`The requested item ${card.dataset.itemId} no longer exists on Actor ${actor.name}`)\n\t}\n\n\tlet targets = game.user.targets;\n\t// Don't roll for all targets if its an AoE, due to only rolling e.g. dmg once for all targets\n\t// TODO: Maybe add target list or chat cards for making saving throws\n\t// or not, since it would just spam the chatlog.. hmm\n\tconst areaSkill = Object.keys(CONFIG.DND5E.areaTargetTypes).includes(getProperty(item, 'data.data.target.type'));\n\tif (!targets.size || areaSkill)\n\t\ttargets = [{data: {\n\t\t\t\tname: \"someone\",\n\t\t\t\timg: \"\"\n\t\t\t}\n\t\t}];\n\tconst spellLevel = parseInt(card.dataset.spellLevel) || null;\n\n\tconst template = 'modules/mess/templates/attack-card.html';\n\n\tconst attackData = {\n\t\tactor, item,\n\t\ttoHit: await getToHitData({actor, item}),\n\t\tdmgs: await getDmgData({actor, item, spellLevel}),\n\t\tsceneId: canvas.scene.id,\n\t\tuser: game.user.id\n\t}\n\n\tconst autoroll = game.settings.get('mess', `${game.userId}.autoroll-selector`);\n\n\tlet rollMode = game.settings.get(\"core\", \"rollMode\");\n\tfor (const target of targets) {\n\t\tconst allowed = await item._handleResourceConsumption({isCard: false, isAttack: true});\n\t\tconst attackTemplateData = {\n\t\t\t\t\t\t\t\t\t...attackData, \n\t\t\t\t\t\t\t\t\ttarget: target.data,\n\t\t\t\t\t\t\t\t\tflavor: item.data.data.chatFlavor.replace(/\\[target\\.name\\]/g, target.data.name),\n\t\t\t\t\t\t\t\t\tallowed\n\t\t\t\t\t\t\t\t};\n\t\tlet html = await renderTemplate(template, attackTemplateData);\n\n\t\t\n\n\t\tif (autoroll.hit || autoroll.dmg) \n\t\t\thtml = await autoRoll(autoroll, html);\n\n\n\t\tlet chatData = {\n user: game.user._id,\n type: CONST.CHAT_MESSAGE_TYPES.OTHER,\n content: html,\n speaker: {\n actor: item.actor._id,\n token: item.actor.token,\n alias: item.actor.name\n\t\t\t}\n\t\t};\n\t\tif ( [\"gmroll\", \"blindroll\"].includes(rollMode) ) chatData[\"whisper\"] = ChatMessage.getWhisperIDs(\"GM\");\n\t\tif ( rollMode === \"blindroll\" ) chatData[\"blind\"] = true;\n\t\n\t\tChatMessage.create(chatData);\n\t}\n\n\tbutton.disabled = false;\n}\n\n/**\n * Autorolls hit or dmg, depending on which flag is set and replaces the template string.\n * @param {Object} autoroll Defining whether to autoroll hit or dmg\n * @param {String} template Defining the html template where the roll should happen.\n */\nasync function autoRoll(autoroll, template) {\n\tlet card = document.createElement('div');\n\tcard.classList.add('message');\n\tcard.insertAdjacentHTML('afterbegin', template);\n\tif (autoroll.hit) {\n\t\tlet toHitBtn = card.querySelector('.mess-button-to-hit');\n\t\tif (toHitBtn)\n\t\t\tawait rollToHit({currentTarget: toHitBtn});\n\t}\n\n\tif (autoroll.dmg) {\n\t\tconst btns = Array.from(card.querySelectorAll('.mess-button-dmg'));\n\t\tfor (const btn of btns)\n\t\t\tawait rollDmg({currentTarget: btn});\n\t}\n\treturn card.innerHTML;\n}\n\n/**\n * Hook onto the Actor Sheet rendering, modifying the listeners for the roll ability and roll skill check events\n * @param {*} app \n * @param {*} html \n * @param {*} data \n */\nasync function actorSheetHook(app, html, data) {\n\t// TODO: Redo this with proper methods... this currently ignores the cool new modifier field\n\t// maybe just ignore replace the abilitysave etc functions\n\tconst abilityMods = html[0].querySelectorAll('.ability-mod, .ability-name');\n\t$(abilityMods).off(); // find smth better here!\n\tabilityMods.forEach(e => e.addEventListener('click', function(ev) {\n\t\tev.stopPropagation();\n\t\tev.preventDefault();\n\n\t\tconst abilityId = ev.currentTarget.closest('.ability').dataset.ability;\n\t\tconst label = CONFIG.DND5E.abilities[abilityId];\n const abl = app.object.data.data.abilities[abilityId];\n const parts = [\"@mod\"];\n const data = {mod: abl.mod};\n const feats = app.object.data.flags.dnd5e || {};\n\n // Add feat-related proficiency bonuses\n if ( feats.remarkableAthlete && DND5E.characterFlags.remarkableAthlete.abilities.includes(abilityId) ) {\n parts.push(\"@proficiency\");\n data.proficiency = Math.ceil(0.5 * this.data.data.attributes.prof);\n }\n else if ( feats.jackOfAllTrades ) {\n parts.push(\"@proficiency\");\n data.proficiency = Math.floor(0.5 * this.data.data.attributes.prof);\n }\n\n // Add global actor bonus\n let actorBonus = getProperty(app.object.data.data.bonuses, \"abilities.check\");\n if ( !!actorBonus ) {\n parts.push(\"@checkBonus\");\n data.checkBonus = actorBonus;\n\t\t}\n\t\t\n\t\tdata.parts = parts;\n\n\t\tdata.title = game.i18n.format(\"DND5E.AbilityPromptTitle\", {ability: label});\n\n\t\trollD20.bind(app.object)(data);\n\t\treturn true;\n\t}));\n\tconst saveMods = html[0].querySelectorAll('.ability-save');\n\tsaveMods.forEach(e => e.addEventListener('click', function(ev) {\n\t\tev.stopPropagation();\n\t\tev.preventDefault();\n\t\tconst abilityId = ev.currentTarget.closest('.ability').dataset.ability;\n\t\tconst label = CONFIG.DND5E.abilities[abilityId];\n const abl = app.object.data.data.abilities[abilityId];\n const parts = [\"@mod\"];\n const data = {mod: abl.mod};\n\n // Include proficiency bonus\n if ( abl.prof > 0 ) {\n parts.push(\"@prof\");\n data.prof = abl.prof;\n }\n\n // Include a global actor ability save bonus\n const actorBonus = getProperty(app.object.data.data.bonuses, \"abilities.save\");\n if ( !!actorBonus ) {\n parts.push(\"@saveBonus\");\n data.saveBonus = actorBonus;\n }\n\t\tdata.title = game.i18n.format(\"DND5E.SavePromptTitle\", {ability: label});\n\t\tdata.parts = parts;\n\t\trollD20.bind(app.object)(data);\n\t}));\n\n\tconst skills = html[0].querySelectorAll('.skill-name');\n\t$(skills).off();\n\tskills.forEach(e => e.addEventListener('click', function(ev) {\n\t\tev.stopPropagation();\n\t\tev.preventDefault();\n\t\tconst skillId = ev.currentTarget.closest('.skill').dataset.skill;\n\t\tconst skl = app.object.data.data.skills[skillId];\n\n // Compose roll parts and data\n const parts = [\"@mod\"];\n const data = {mod: skl.mod + skl.prof};\n if ( skl.bonus ) {\n data[\"skillBonus\"] = skl.bonus;\n parts.push(\"@skillBonus\");\n }\n\n // Reliable Talent applies to any skill check we have full or better proficiency in\n const reliableTalent = (skl.value >= 1 && app.object.getFlag(\"dnd5e\", \"reliableTalent\"));\n\t\tdata.parts = parts;\n\t\tdata.title = game.i18n.format(\"DND5E.SkillPromptTitle\", {skill: CONFIG.DND5E.skills[skillId]});\n\n\t\trollD20.bind(app.object)(data);\n\t\treturn false;\n\t}))\n}\n\n/**\n * My own chat listeners\n */\nfunction chatListeners() {\n\tconst html = $(document.getElementById('chat-log'));\n\thtml.on('click', '.card-buttons button', onChatCardAction.bind(this));\n\thtml.on('click', '.item-name', this._onChatCardToggleContent.bind(this));\n\t\n\t// lets just use this for even more listeners\n\thtml.on('mouseenter', '.mess-chat-target', onMouseEnterTarget);\n\thtml.on('mouseleave', '.mess-chat-target', onMouseLeaveTarget);\n\thtml.on('dblclick', '.mess-chat-target', onDblClickTarget);\n\n\thtml.on('click', '.mess-button-to-hit', rollToHit);\n\thtml.on('click', '.mess-button-dmg', rollDmg);\n}\n\n// Only overwrite stuff for attack buttons\nasync function onChatCardAction (ev) {\n\tif (ev.currentTarget.dataset.action === 'attack')\n\t\treturn renderAttack(ev);\n\tif (ev.currentTarget.dataset.action === 'damage')\n\t\treturn renderAttack(ev);\n\tif (ev.currentTarget.dataset.placeTemplate)\n\t\treturn renderTemplate(ev);\n\n\treturn this._onChatCardAction(ev);\t\t\n}\n\n/*****************************************************\n * Mouse Listeners for the target img for chat cards *\n *****************************************************/\n\nasync function onMouseEnterTarget(ev) {\n\tev.preventDefault();\n\tev.stopPropagation();\n\tconst token = await getTargetToken(ev);\n\tif (!token) return false;\n\n\ttoken._onHoverIn();\n}\n\nasync function onMouseLeaveTarget(ev) {\n\tev.preventDefault();\n\tev.stopPropagation();\n\tconst token = await getTargetToken(ev);\n\tif (!token || !token.visible) return false;\n\t\n\ttoken._onHoverOut();\n}\n\nasync function onDblClickTarget(ev) {\n\tev.preventDefault();\n\tev.stopPropagation();\n\tconst token = await getTargetToken(ev);\n\tif (!token || !token.visible) return false;\n\t\n\tconst pos = token.center;\n\tcanvas.animatePan({x: pos.x, y: pos.y})\n}\n\nasync function getTargetToken(ev) {\n\tconst card = ev.currentTarget.closest('.mess-attack-card');\n\tconst sceneId = card.dataset.sceneId;\n\tif (sceneId !== canvas.scene.id) return false;\n\tconst tokenId = card.dataset.targetId;\n\tif (!tokenId) return false;\n\n\tconst token = canvas.tokens.placeables.find(e => e.id === tokenId);\n\tif (!token) return false;\n\treturn token;\n}","export class MessSettings extends FormApplication {\n\tstatic init() {\n\t\tconst isDnD = game.system.id === 'dnd5e';\n\t\t\n\t\tgame.settings.register('mess', 'actor-item-sort', {\n\t\t\tname: \"Activate item sort button.\",\n\t\t\thint: \"Adds a button to actor sheets for sorting all items of that category alphabetically.\",\n\t\t\tscope: \"user\",\n\t\t\tconfig: isDnD,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean,\n\t\t\tonChange: () => location.reload()\n\t\t})\n\t\n\t\tgame.settings.register('mess', 'better-draggable', {\n\t\t\tname: \"Activate better drag'n'drop workflow.\",\n\t\t\tscope: \"user\",\n\t\t\tconfig: false,// Change if implemented\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean,\n\t\t\tonChange: () => location.reload()\n\t\t})\n\t\n\t\tgame.settings.register('mess', 'prepared-spell-tracker', {\n\t\t\tname: \"Activate prepared spell tracker\",\n\t\t\thint: \"Adds a tracker to the spellbook tab, providing a way to track the allowed maximum of prepared spells.\",\n\t\t\tscope: \"user\",\n\t\t\tconfig: isDnD,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean,\n\t\t\tonChange: () => location.reload()\n\t\t})\n\t\n\t\tgame.settings.register('mess', 'add-scrolling', {\n\t\t\tname: \"Activating numerical field scrolling.\",\n\t\t\thint: \"Lets you in-/decrease numerical fields in the Actor sheet using the mouse wheel when focused.\",\n\t\t\tscope: \"user\",\n\t\t\tconfig: isDnD,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean,\n\t\t\tonChange: () => location.reload()\n\t\t});\n\t\n\t\tgame.settings.register('mess', 'modify-rolling', {\n\t\t\tname: \"Alternative Rolling.\",\n\t\t\thint: \"Changes the way rolling is displayed and executed for DnD5e. Reload for all connected clients is required for this to take effect if changed!\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: isDnD,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean,\n\t\t\tonChange: () => location.reload()\n\t\t});\n\t\n\t\tgame.settings.register('mess', 'modify-templates', {\n\t\t\tname: \"Activate modified templates.\",\n\t\t\thint: \"Makes templates texture fill scaling instead of tiling and does allow the usage of videos as texture. Reload for all connected clients is required for this to take effect if changed!\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: true,\n\t\t\tdefault: true,\n\t\t\ttype: Boolean,\n\t\t\tonChange: () => location.reload()\n\t\t});\n\t\t\n\t\tgame.settings.register('mess', 'change-placeables', {\n\t\t\tname: \"Activate placeables changes.\",\n\t\t\thint: \"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: true,\n\t\t\tdefault: true,\n\t\t\ttype: Boolean,\n\t\t\tonChange: () => location.reload()\n\t\t});\t\n\t\tif (isDnD)\n\t\t\tgame.settings.registerMenu('mess', 'templateTexture', {\n\t\t\t\tname: \"Mess template texture configurator for DnD abilities.\",\n\t\t\t\tlabel: \"Configure template textures\",\n\t\t\t\t// hint: \"!\",\n\t\t\t\ticon: \"fas fa-mug-hot\",\n\t\t\t\ttype: MessSettings,\n\t\t\t\trestricted: true\n\t\t\t});\n\n\t\tgame.settings.register('mess', 'templateTexture', {\n\t\t\tname: \"Activate placeables changes.\",\n\t\t\thint: \"Changes some behaviours of placeables, like preview snapping to grid. Reload for all connected clients is required for this to take effect if changed!\",\n\t\t\tscope: \"world\",\n\t\t\tdefault: true,\n\t\t\ttype: Object\n\t\t});\t\n\t}\n\n\tstatic get defaultOptions() {\n\t\treturn {\n\t\t\t...super.defaultOptions,\n\t\t\ttemplate: \"modules/mess/templates/settings.html\",\n\t\t\theight: 800,\n\t\t\twidth: 400\n\t\t}\n\t}\n\n\tconstructor(object = {}, options) {\n\t\tsuper(object, options);\n\t\tthis.object = game.settings.get('mess', 'templateTexture');\n\t}\n\n\tgetData() {\n\t\tlet data = super.getData();\n\t\tdata.dmgTypes = CONFIG.DND5E.damageTypes;\n\t\tdata.templateTypes = CONFIG.MeasuredTemplate.types;\n\t\treturn data;\n\t}\n\n\tactivateListeners(html) {\n\t\tsuper.activateListeners(html);\n\t}\n\n\t_updateObject(ev, formData) {\n\t\tgame.settings.set('mess', 'templateTexture', mergeObject({}, formData))\n\t}\n\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/templates/roll-card.html b/dist/templates/roll-card.html index 4494e41..04c07b1 100644 --- a/dist/templates/roll-card.html +++ b/dist/templates/roll-card.html @@ -4,7 +4,7 @@
- {{roll.total}} + {{roll.total}} {{{tooltip}}}
diff --git a/dist/templates/welcome-screen.html b/dist/templates/welcome-screen.html new file mode 100644 index 0000000..f3a13e9 --- /dev/null +++ b/dist/templates/welcome-screen.html @@ -0,0 +1,41 @@ + + +

Thank you for using MESS!

+

Make sure to read the README, to learn about this modules features, since there are quite a few and they are not all connected.

+

Bugs? Issues?

+

Read and follow these instructions. +

Suggestions? Feature Requests? Feedback?

+

Currently planned features (and bugs) are on the GitHubs Issue Board. Please check the issues if your suggestion does already exist. If it does, give it a thumbsup, else create an issue for it and label it as suggestion.

+

The issue board linked above can also be used to keep track of current development.

+ +

Best regards,
Moerill

+

+

+

Want to support development? Click here

+

+

Changelog v0.4.0

+
    +
  • + Important: Automatic template textures for DnD 5e are now independent of the rolling mode! They will get activated when you activate the scaling and video textures for templates! +
  • +
  • (Hopefully) fixed the module sometimes not properly loading, by removing race conditions created due to dynamic script loading.
  • +
  • Improved the roll controls above the chat window-content +
      +
    • Changed the advantage toggle to now using dice to display the current roll mode
    • +
    • Added a situational modifier field, which gets applied to all D20 based rolls. (attack rolls, saving throws, skill and ability checks
    • +
    +
  • +
  • + Restructured a whole lot of the code, so its a little bit less of a mess... (Even though this is the modules name! :P ) +
  • +
  • + Removed the default 5e context menu for applying damage, since this resulted in unwanted and confusing behaviour. I will revisit this for the next patch and add some option to apply damage using the rolls. I'm just not sure currently as to how i want to tackle this. Feel free to add ideas to the issue. +
  • +
\ No newline at end of file diff --git a/img/roll-toggles.png b/img/roll-toggles.png index f94ae2ab784e481fa53e54b645009a0cab8e7ef1..6596d3d3697ee42aa03d5e0915949edcb9ff0f3c 100644 GIT binary patch literal 48033 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

s`R)6xi50*_--H~1Ed}HC8_3nc%2IiG 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%0A7anGO-J->fLLuUB&4qjbSp*c zG2G}>#MZSZF4dL2XkFh70uSkv$1-KUuB@%;Vty2vGP~la6E!FQYr*SdRVmZVE>v;C z_Vl^ge2;FPOz+pe*}p2P@d??96^Q8{j@_(*9tUdq(fQFHq2UX`$L&JRHHrJisGOSb6T|&6hU$L-a4E(9x;NXASx(o|uh*#$^YHxEvZ+jaLAc$yd?i^EeYqQx z9ALr3cMYk2J?7Ldy&0Tmsh{f?mLf0sBpx3xe0#fzfIl_I&+F5FV!~H59&X2@B9d~9 zQe&0klU}$vVV}pKRHx%CRjtRl_AtE%mKf8nm(GiuUR2+onccTk_ih)aGutmMyZMnA zH@^FF#R@{d%2Z=b&IR_1yS+X8VS$pE`vm@YRuuMGG56WqXPH~m-96}jrPAegP#9Yy z@x&tJ{$46uuw*@+!ek1l6LM*+0l)&~=sI*_|0Mk0Lgu;+7;o&9D!wX3$1RvJA4xf8 zA3+)cw6dPdj07F~j5aZ+p`iWhA80QExSQ2(d`!bxRZ9v8Sj?0o&o89syk0goxEl1r zDdvFF<#iWMWH-<7BSXOdR_y$7zuC&#dMgy{PYk|!OmNzX9QfGfT4KF<#DCq(H5}h( z(tVt8_3ux0=s*YGJ&qGrlazKC3%rhZ`-g$mFb}j-k`ZfrC%&4>>44aD8Lz(#Qn_k7 zto-K3kq{(V3APC8q`itc957s|UvbLN`xB>7l2NsAKS=1XE3o;-Y`at~{}SgaIxaY( z_1^r)Vf`KKb4`)@Cc;z+fQFX$=aY0+V<_O%d+`zx%XGiDnu{d1?iL2?e z>jV)4EK01wy>XY@%b9dJJaS4Mse%fg<@_tzXRU)Be8h|7Njy)|H9e&AQ-?iIF-LeS zGPB3m7=tq9 z`_TThByj$3v-?RNP$UiAc{bnPAD%9YQfK+?w7kwE`lHQhC#6H`*b zVi+3y>^$D!(*XKvT-PCQZEX#12M<>)X`9(fmYE)e+zfyPj87G}gw!1>8@Nv0Wox zNw2CV_=j@EVY}018h=Ok(J@cRViLWw69_jPvuuRvO@DxJr@49>Zomg{BG2f6s?c#Ebg_b}toM=XKS)#43Of!Egb8G~Xha%ii@b5e zq9hjc+%-6AP?<&kLCIQ6;KWV%2TFett2l8?t>knin(rVyeZsE0n9_2(~};)1!sY7ksy zwk~d=cGOA?@oA9K88{4gP+ukt-<}#d&GnWb`gL_%uM0@_yxre^;u`qG=f1xl=-rel zemzdpK=exu`ET;np8C zdYki~^9W;EFsleooPWg(+C^V6dx^I>ksDu_%fh0qm`Vs+JuP&iAu;G1J_>%n!T_xYZK@B^O($Q?wJ>p|Xb;2APCrC@M!yxmyM z+}76SvA3Y9{c#OzH|kF$3HlYnkrai*uh{g>Xw?n8KR>GBPBaliQfZ)_0*zPc-}W^e z$13RW#TTfu9FdBRTdxIBQryhS>wb-UjvK)2uls+z0X6~D@p?R-?|K9_&cE+U(6gr8 zNR3e()RfH22t@6?z!8`-arp)Eq??Lk)x(Z}Jp~5zf+!qhiuv)N(Q(zrhSA&#jEe-`ma;2~CmoOLZ*HeusVU)?(;(pn zz;U3L(u5q`%#Sr!Q&Z#6jf}Q3NACr){_g#g^zy0$lwwYs47<(AI9}`bHoopM&gAm} z)Q4C1?o4MeUl=gP!S;Qf7coTe{tlx0 z-BL_t@WehC`Z#hqTW$~NHn=f~ygh-R?o4B;M2(e$P1n@PBvg2&@%n1Tu?GL!d6Yzm zKKZQMr4g7bO|B=Ih?6zVUN4)AlvR;G_-b(7ODc#R{*3)LZ|*30O$FC=hv;rA|~5a?^L~l z@+AN@jnh5p;zc?|Xt=kIh<;%LHfG0!kK25ch8uR+6(J&Gr%(M(Ow-=Cy7qhd@U(QR-6YdEy$flVV}VcKYRzSIdyfY01OzR$b| z6-sca$bOkshaeLrjkIzjbdj>5pfD>1^CF=`KJ~51J~JnW_$Oz!tG~bhb2g%cCvqw0=5EMx!<@3s7^c&)%nWvg#@kcO z+~IWL5(s5C#(|-R>eVEDIEC3!;N_HC^%Y0jfX^Vt1X*QtkQ^R*JuB!U>`@dxCHhP| zSXmqoR;vjvB@xT_2Dd@5rzkWPN5@9GEZqi+?;A_n^%+_kToU$!2x52b^m$mq0L1g*7~lT zwA64}xvMiNqL9&XniBing!)J+;eDV)deAT=h5gE6c^P;3xE*)r*5-mt@j*PJw%E^* z%C46hjhFe0X5@qVOa1k525l#vAYm^~-RAilz0Tx58-PPAgT1ah=picDDws=p+nSoPN8(z5tB|F1#3<8}E!(n6S| zji@$-oZpDj7Zpg$GF}GyAaPumIFszaQ_!&VfmmFAl;W=|ls$e?VI5NG?mNMg2e(>R z(i`SMO2AkE2=O}Rg6$|-ct7;Q(gxJ(%klXrOtxwJ*kOn7U=azlxT(3t;~~e%fA#Y6 znV1@{c;>Uht_{^W8wsQ>7yk=GMj{F&6f=b|=vFM)1Q%2l9L9d_Tc$?QV?_u|oYjX# zGNqx;RSjwRhOM83OIPF*MK;-)W0#bcm0E#R)?AxBzUN|WJ&Y9dFseNqfN&zGX@ z9bN>CczR6q2$;sI!uJ#s@!jjs17yK$0~#p9jlEire!+lUg$Z^1t~L-4Jasi^N%C)G zyUWEGn<&*^;2Mi~^7BFen#PUU9IJti9bjpSWxc<;j>dy}bxNm29@#@mE3dLC&zQdH zP`g1Gj{kv(lXHME=-7y?)%hlzVdY0h^L@>YUd^dhPe*|Y0-z_b}Nks>fDSpxtt#{MwWgQtVs62M928xy5F zlmUpoo}c?`E!O$hN`6=dN;RB(pkOI zK#O7-GZNZGF0+f{1&*NMhrtsvEXj4bLRFcTI4|$cSrPGMrt}xb+Y*G0HlnpN`Pdxb z2zQx68|J=Oy9BV{`y=CK-=ubUEK~Yj-IBi>zo|}~F{u0?sO_M(YmQ%bif{zRX;ZU{}qr_MCz6`iP{uFdkH zMRV{UI;!qu+dn^MOTRobKq>mCGI15zGFW(G>kDd7kgWV!@kX#A;Q0;$DD{Gpl9J+@ zAR&(Nx`?<1XU4vPCi}MbcI|FIA;9HrS#{d-KVEbG z*H#oT1wJX-X?0h;H1f?m9wy>{(RP+WSw(NV7ePQpN$C)zyE{a>rMp48yQLeXyIV>^ zx+H~%?nb&h9-4RYpLx%jGv|CcGv|B~kzw5X*?X;Z-}m*quK&P!h)JnX%}q$gW zEG@TM7T_OyD!B@KqoazOcp7fI_Ov=kFdbOBy;9nK0zNLsnM2Stp@?jl%3>G^@rx*0TiuXbPczjAu+qtug6 z6JHayLWE0AxN`oq@SnLf%Ia3SMVEb>+4+M0ue!MAm1fO-7|JP$c#RI%?t~t`)-(^r zj9Eb5+kpYT8mFuOaC-Fi*?q+^*`gBevWuw?Jl|P2`R8BTX7AU&E^_1KOTxWunRCiU z-PtdPrHJ_Q=H0tcmplbxcwPoEl%N3p0ODTAW~gD>qkX{4cyC(;9h~FJviU|1-;nf55jt=4C|1q!!hbo@c z(@%2xSwV$22~vP4h~l4|IM+}FBo?+jw~uoQDxLeGSi^Ss;t$Ua ztt1`yP$147o1X4{O_APlJ|Zv@MZm+@YdfuOati9$v8ky95|PuNp`ZIV?ROg-T?4j; zH8eF-cwDS+y-?tQSflFO5H8TsPS_3Ln|8Z{?eti=ucn`Sl>83deyJJX=m4=0!?K@7 zZ$BaWCI98DjpRxVnr1>GVoSMt-8aE){<87QTc-D|OCEn__3(p~ZyOKzo`_iM&T2&J z&#-8)Sa{zw+$}RqB-pIB9=E4t;j=`KDp{#QI-}4_?q||e^=YvncBP9r-Bo|En{h{8 z>6r)kTP;YSl$b7MYYaR@YkfNTzsB~+HOrXyQ71ICFq@+OH*9aWS{7H`%(38(?qRT( zAhf?=y^vLa%q!+*uHJ?S5Ng@=^&B6Dn--PWx1`b6?QKxM&ABCcKO;YVL_X;N5qe>J z)=x13HfaCs>XwN#b%fiw%KG>CGfq^hWUa0K9Mk09sJdsBYYQW1zWNA zNYWUzT3Z`s0L`4)Ib4G5h<4>KE!-^nuN3|cORa0l>zIw8I@+RXe`Kc-m0K-!pf_HP zZ(RI!-v?Lqhs2GGrJ2WV`$(I2@~gN()prq6RwfE^!HTtUhB?;?i(?GKoa5~puIv4tk(=*u*N-~+> zo3&;WoQJg&C%nf;?#-IuZkl0xpv^5$`LKtn9d7tOAR`p9)Vl=Zq^E!W%&#b~#n~HSp zaZ(VS(~din%XtCM|Dr5?sAJ#hSx?gm2iA*`AWj;2-h6-J&6f& zJ+S-wy@|m80QSbmcbRoscL9?mBPI1b2*Y^uYWcFFTY?Q-`3pkVf4=eiF*j2=xXjVx z97FF0crn98fGidGjL_<~rMsPPF}a-?f#K&fvIL};&n<-Ko+~4g+iD;dfZB?{z!O-2 z0I;cF=^C(oT&>oCQa}P8CPi7^P2UK1+&;k2E{nFlFDurdoUF2u!ntyF}TE z9jnr^5;2#9bf$od4sbZP_Pb(2P%^?W!RZ z;yA6~{_DBYf9`X}l1ZI-Wrfo>g#e8USR%}T&kxX_zQA``v;&ZC5(1Iuk5_LoM**LB ze{-&wWB+)wT?5{JOj@7IGLT~w;g6g=9aej{o_ z3)1}K^3W65R$ZC#u+cF>A(=V|pJQ$9Rj*lnyREuZMQwm^l>3^WzPoa+&|nFl>sy5L*_Kv6*(8-{4UD{MRb8iSrUgq|mVa095qzO*Bt zrsKXg*H0EH5JVZBn1~~xhExMlX)X}kg4qEo91KOKD~TY=FF>+_k?C_+SrCQM``-0= zSp}C4r1orJN|giFcrxIq!fh+e^4=9me+fsK>PtfAf)kzISNtHvt3F?TH~% zolxUsa1xDDRrb)ywEQBIF+D>)e(xpC5i!;+Y+*)1XjiD=8}HW~7j^_`vrH&K8@bMsFg8E-8`#3FemRfu zK>=a_x(^g#B!u4A`Y%9-FRd*dKp0?lR`+}`omAoh3Vblp0^>+DIGAYuTS|cVok19Y z0I$|@4SFyaC);Fc;D!07xbYisUH~ttJ#@)sVrgk8PL97HfDnLK#3wKiMC6d`DHGh8QD*|+Y>c1?GkO5lZ0!T3A>&+O>V6gSRlFA zjZ~LCkn|R`b00~dAEz$FoPrjmsEUP6hE~?*EGAGTxrwT( zs=Bw@3ToJfb0~SJl4VL9pC2@%JW9UROHoC2yiH#Ndz=t<5PU;;9tD zXG#)f2$0(6CA+fimKuDF?iZ$@3{ZGf$)r1X1fo*4#CJ-DKS-xZqJFJoBdPV03d{dP zSGvXKHz(1~z%wOePxNg29{L9gQR?KU7W@m&FaaM96>t7FW~~?YwfAlCErc5W%Jm&; zY&*2Ic3TLZ+hse0wrh4cd?NpM?R7tw+DA42{;jMxhuy5)lotZ_blU}*s;&oJFY}@^ zq?}f*;Ri+HQ)1>3VZpmA=owOhaZKo=Ho!-DoDakWfUtSm6r7*06N%Fj6ER=VHy>2v z4&}$#5-BJ^L^N%|Dlm~)HfA8VSta!NPn zw27YKhB%P|o!EE+7*8gMXrQaO-DN4T=?w68Y86&;)9414lk;<*{&c?x7> zc?^iwz7*lVzJ?~d=)LtW5Z9&dlX<%`;KicTb(htg1+{iXq&S%!V~nlXlkqXR#W^_V zy;p$YO=3i(Obo;DO#0H+{YQQ4C@yyhs$9l?u zY=WiLlV7exi6jZ~4)5NBNr6)M=nPD&g;Z5%evE*XZDt0m4=Jvp$e;?F0sb&7bo1lL zr$oUjwFo+7E|qGNT*~g)v13)|YhVj7JMgB9ahbQ91i&HXFp_)w{WX8r&Uh_78cMK-EY*HD-?Jr67wzL+m8EiB#}@_)B6Eoo$C|C?TRhJ$fhnulYEGj(h(L$ANv6d#Sh2$ zQX10DC?iet(lNC#k|+Ss3*U@z=A&f(A;^ziNOdDx0gr_OsU&8HEos1Tt2fvZ1CJu$ z69*OsuDgR8x21iZzQ>0<^%hsA7thWor$5mX_&lx{85uhOw(hzXN+Dh}5rM-(KHSlg zN*dQciE*<>n@CCfS`88q7g%H`#GU3?OQNe?OJnBrE(F6uY%$cxR@2ZUmR<`j`{&r+ z?Xt+&?#jMpQbcG&8g2*m8-?LRPT6-K6~7nNSxtSWUP+QY5P4W z2%b4b`W~Qd)CpnQOP1{6`%N46TtMdqCK-7(HNZ3HnSuj`Uu`Za2)U$}spzVjBd`lj z+RVGGZA3^-UWK8CJPp=?9|NQ-U{(-T=*Ft{acDuW>EdSNE_&3?mL;z*DbsrMdPfo48vNV`z!TU z(NVshQp2?cTZu>`SvP*^DYczyUJ2Y5(P+^+{Ut-wDf0^Tnk7RL)?0>_a(8c6sT95o zhOKxN0OwV11`(}oUNXxDc`TIoWY@Ns59^_IK;jOYyMgU%W%ktgscvGvdU7a zR~swOS`-^Mh-L%OJgqUqzH`3!GVF>9%!@ru=qMkK3N82wa9>Ny2p2nbN(sncyL3)X zGNx?OECsZuJd3B;YrN1vaU|-SdQqM5wTUWJ`5&E|MN%e`?_8D5HTl`LIYiP->W7$+y6SXt3e!v*=m zn59ADrb!Iy0d!%fKry=Ni$EBXC9m~a9(&HRPLzoPu!gs}4RHZZU_49=UG}g!kW7#) zxNZQib2R{ha2o>gDI_I(1x<}(1>GY91eSS$nIf%L!w*O9A)wAP-vR)mRtK;bFbFIg zJ8~zStuyYkgA0{+Ym?h?v$N8!V*PH+rB)cK|E0u*DDddP2v;lyk0}M!uyEYVmPUsw zdK;4x%l%L7bugF7SySfwE&S!-DV`bo%i-USmTLXcCbVl_m0lD@mj~Oe3*LSCUpVgk z-l(Yc{t_C1b$eOqXNY6OY(|~HEb7$h(H6XY^I4oeDV#a3BAiHcuKy!qaRYroOlLWS zCOB{Wt!SSkD|+!T$!;hOy0){2T}HP;Y+xSD4}+VKmt%9R{YSgs%jzaslNHyfa#1!s zQzaCSa&~0ZZn)X7dw)Lo$`SrU@x^*0EJ{36=L^Ia z=qpeIeD~sNrb2lFO99XWM$s$Qjtt;YO;Ux={ZR%x!9=VrMbTW)OKnyvZW6#+Vb_fYl{XY>my$smBo{Y@@kIMW+qJ?G61iJXhI{!FH zLv{8KGYDNB)#=o*LP~?_5+<)1PhepovikF8U%EPx|JW<26-Mgg=EI+HT8BQy%j_iB zLS7+~$kM*d0&aE1C>@KMSk%|9SAAZw;3>dslwnPtwwxm;BMM zMT@zf{p;XfLRI=xOL&g~{^&GM!`9j~qEqNxsBylFC&3dsMD}t;|CS$52);8H?DbN-Nwm&(* zxV+4G`F_NjVG7?PUd#cM==84Bnj#lko?VDJB8c3 z{FkFD0llzh9r<u;;ul0lL_ZXYgrL!mb1y_8g%WY0=1sH!ekzLU z0NxQYM9*>M`@qFABRP_EsqPIb?|R*(2ig*4I7`U4i@%u6C9J?}z{&WMnX&|o?R!cr zs~j)7C+8|iUohcZ$dWBr!g<8av3q$h)IGaZoGYG-nMv&l+o}0w&uIPQUMODYmT04v z|Are(HXt#2YhXuesFCVJQ=$~NWO4!ZJCFU?^39EN{x)Ig*0qnU#-m9$A8vXOYoi7B0AZw*wrJ(wWsDKxZrA*Y`Y1u7-Er^LzFq3Km^Y0v zB1ZhZeIrEl61I!+H8njkv6@~0yiz$aOF&2I=dZrNoi`+fD{}9%c?A({UN77`zii3M z7lrCFtL{1p9DMWoJk3owbK{+pW)tF53&yRaW2c2i+gYppa%U7~@oZhKD{$?K)sIrm zHuFCv3|3;5AN%KO_ljKF&1Ry zUuEbi28hj=_SBX_36nO=XD*|gV8Olg}%P>R?#1X1KX>(@Vl*5 z8=KCNm}%exg!Le%pFeT>YyYktNO+^KHq{^)fLLdlCe5AaX)Tn%(l9$^k&=k>2Q~Z6 z0V~yqaAlOAg2m?P_!MC|INvo(gm;|S{wPKXL4ZUBpDFj-_Qc{~>L<;#hqdXDprrPK zbP>NuZh~^uzi2|EJYf+z!*#uEGNJntwn|50<2Se7PEWr{8JQ$Za$!4eJ+wy(uo7ii zED~V|nt#mti#l3lk_@4g2*$eeI&vf!(a;Z#I zny*wc1sr~M7Mlx59_ao)caq2?%|MmDYLcop++!+hUqSb2_c=K4NVXg3 z&mq$7=%}-V|M^>kio!rX#LNEjCDKGUGlG9=lT_gTwE3g20V<8?zf@G$iE~?p_h7f8 zC0UGv`}6xX)pX)|{)tzmXr3WWx)sVbyj_ez7-GSe>An%aww82*5qEolI_sWF=UX9g&dHgZ`pqHM1y`>&$APE zKb4@xL^Une^Ra?GB+JUHbTYPwI{N|G}qR8eE#PXZu`iQ(7Qf<Y)*+wTvKNr5~s+7{+ z3w>)Y=^ER>5Nby@W5euBdXPzBuZ4zODCOWtC5MJp@HFkpuC2kU6)Ia@HUsA$r^*_dLc30e8HC->Xbl<;hniN@_1^!;3uwx{hND|)J|L7F!BB?1tVV~~$H8(vr{RkH)tL)2YVmADi z`6T}Uip@X*^0Ff88xX`+&4`5T@SNPBW--b zfk-3}L70j1)UXGiHc84~62eHC0FiIL-n#@wIo)lo9>K~A94lArw`Jv!V{QKHOqW*b z_^FHCiFJ{3x)q)i6^3CP69mXY1_To6jv?3&8EGdr3PzaTs{B=1ifIf zG6csM@L6Dm%hOgFiGAy{`y~S*EfcQFRk}o|k{ifo*uUX7q1XC*SK)^2^U&YEIkE!f zANDv$yCUN3U?6e}@VUQOIV}gyZ9~cfI`taZ_{J|0JkEN&pp(NthK!=jBp#-8eOlV= z_s%@xY&;(XbUr|dZ9`X?&F61X!X*$Ewnwt$EG#S{dvZU>REwU9_xIcP7uy2S zFaJ#;g_|BA3LY8GVXBxal)z8zDd!DDQo5JT9O^}rKRS$;H)r3j7ukY)Q_;|15rQa2DOte{GK?SZq5rCc|&rMA_3e^N1cOCyL}Mcai8wRFZ!Yv|ymx!(=c`{`SI!V#YBQDxxapZM!q^ znU!c2mQ=;ad99(RKTw$TU|l~V`~1zyba-!)?yNBsI09~G!}(Ej!-I*7?V$(Z%I)lb zYXMgJ;``%n#OY|lWmtS#7FIXboY@O+V^)vk47^hFZiUJ7GJo`+#WdX?V+=vykap9G zQv})mqUC;Xscn%Yq|iF+=UM>!EgHw#8{5#rX@5^?O`{lxe#M2R9sv$zPOy zMk|bw=i=08fPxUd=ElsX%grajtGGHag(htr;K(wObA_-v=hHu(Z;VSZ7y~;iro#$Q zo*M!?%V*}VO9R^Pv+t@L>nx(Vv8ExhvuWB#CIaB1XmPEvB(yBawI;_RB7*RVK~QUQ zDh+%HD?m$e#HHBmZlmdlsAF$xZjvaPU}a-HyP~Ps7!RjkI|GQyA&{vr(6^^cf$3ZL z${D8ggi&|MH+XpXWOgTWCu0BoMy16!vIC*}<@X(Zbs4FwJ5|EWA)u3cL|3?<=olHJ zf;fLR9*@`5dbuqCwAMqZuTY+NtjWrY`7UmvoPB)GPor7P*7C8Z7-r&Dd`k>YTBZzaw`f>rRh_oQY^!VO&Mj4y}+|on}|2Z94VNOWj9ilXP!xMRGYpd)QJRD9`Qf zx#xPR?xt@0*@re4aP33756X*-lhtL^HD9KZ*nhSiLl|Km_Y8gd`yIZ)j;_p%3}0oG z-EVdsEH>7VM0P0yjX9EbB9dms!TsvGEumyLXFRPGO)6e z)P5~;q*)iC`>>}f#z!7Ee6qV=v5p|nlCb1)b-&za2%et7EP*UARDclDpCkvkjtk31uiv&n&hdQf|^G~z~S!K%w#e- zpYALE69eI$lDK^vPU8|jj0LEdeWRtaM`Lm!mI?Ba&kpgt%kkP4Ub_S+u8Yz=nK_m` zS!O7l4FQrfa_pUSo|Fz5tYd#%oygj%JHRP-q=B z?UMxRfRhaPJ4zBGPTqLS*O%9prluj~$OL&n#M!bdW8*FlrKUHIMPx+kYLGe)M?GbY zwQ6@P&mO+&N#A{Gkp!Qwk6l3L;qK9RbGNeHBb#n?9!T6>HV`Xf zq}j7j(Gm-CHtbLS5vQh!oz&4(>P?rW054oHjf9-t9SamzmVf9I6F9>vTLI}Y0{4tB zj(t|{Qn>OWNA4#CU`Bx&N9=K}>wf*|0V(Tgal7GAOu%3)H!z*ugwBT9=imd#T1#|w`TlL zjU#sZR?jvC&h)Jt3Sj2`nK~i2vOR0T=T@=KW?^`?YQ5n$!^Y$+zyIP2db+=I&&_|l z;&nSwb~{B4%$6zEM))5}_OFNbrtZzBDxXCk4ZxZJdk%O5BD!72h&}VV1{^obfVx>E zlfve5C(RIRYIrCTC;*BGzySG8Yz-T4m8EF8_mhFMluOl7qf;O6$_|sE>kQ9IKR6Ts z%x^U(Smk6ILLb@J=+=$6d~;RLVkn9Sw9pAOL|doI(S3Gtl8w$q99C&h(CDWQAz;1R zyu#OGp?i+|xQ@s1D45`)pbpcgq~Kl=&XF7L|B56Vow2Xx(aCE0bXqMIh8WQ=Z+SbE zzkO?cT)U3#P>Q+Ef+q^y<>{}{IMa*$Qr#!+O0OXUouVHFEc5dXCv-Ne3mhwhKFhA3 z5{<_eX8NE$C$P(2ms7P$IIJ)g4F}BLHSTQn57&K27zhT#rd^DbDqKTD=4n@0rKhj3 zaz7}O$E{v&r+3A4iE-NJzx{4e=_PP?b-Z1Zb;m1+eRAZYJ;bwo`rf+ra@w$sG)5+^ zo^g9)j`wQTP%UvNovUVd|JVdz*T4`vX|6`jV7*tS>Pd$_>E`(9@wH-+?DJAnrqjwD zzOa(3VApt^8}53iY_qQWbQpy0egyO=b(Tv5{WT`%7ZY-QSy`+e?C0BA zf3Vt4$Rsz>z}YxYU?o}~`~K#)Pwy68TI1;g$Jh_Z=_;}p1fmAkmyyvyD;V`(~Zs&v;;O(8yxZJOAhZ?u;9x$$5cq<(oHcg>z%R2Op*K616 zM=j^uwZDh{tp*cSm`V7QoonAcLTdo?-{v+U!@7lgJsQma_56ScVDrv(_2)~+UeVy( zbK2kjfcKt&e-NmLD3c)ZXkpYRymi(Ky)mCMq_g$)JYYSUQ^V^ zzTC&Kb`8}{kw54zDOX~bzB*dy6XQSmLyYCV!Zu_3uLCA=$w%Nt{lktJ=nLER7$?08 zozsGz%AG9UzC-28#A32@&x}q%v}{k-Yf5g+jbUiLHfXz=syvyhT*{y`uz8uNRVueh7n3GeuB{OXV=*GJO!y}Fl8u5D0r?x z|5MRWU?RIP@$K0Wrd>eOfBM8~3A0G^(1qf-%*(|aOojmuMjvq|7fg&0o zNT7`79w4uIqubha2t6E6`1>O9MkdV^)!Xj9{XYH!{;OpkM_gM&Jbd<8QERv04hnn& z0AmD^0DlFS2U`eb={i+OzH$QT$%G7Htk#Bv+tr+j8{m6D)(H>^d7s5)jZRHD4YFZD z$CUPK)o9yXv08sBu`LQ-1YQ|!a2bRTIJGwDsWF>Ht)GOp-(yFH_Pk)ME(nCSLt7Wm zx)zez9LNQl_vs%uYqBCAr09T|>c5BM#vq#|Pls-dJ$I(KJTpR~r~ILjyaM^nbDra- z@MmPWm!l;H+`{n8BWu5p*+8=U=$Yx;`PtIk*~!HpX4f*c^K%aTK;;uUi{naLtI_~H zaJJa0osfH$z8zTFYy+od!=8Zz=?vyTqD6O$$nlBou|tU03eZe*@B=qOROai5c}D?U zY5mE+F+yu_!S9LIR@xtlfN^U4TY9yDy{YzfEMQ)+xY*s%@nUm8uduMWnc~qU=ZDF| zIo?g2h{@S#*aupT+8>hI3EStJCi{Nrkz-Yp8-a}d9Boh14;7z+_^D73uf_Bz0sTFR ze_rh!{mKfsru^g@1~#Sc?$`zFgspSEIJeY{O58k2kWEgH>n{oM|EgIXzkHhQ@r%b+ z8Dz6b6=mBup==50iMDRfNQma`OdiX_;c&*!6N@wemm5LAlPE>s@}v28Zb zziH1-2Rc9V%k|WN(C6L`*b4m3pHM3d+;6O^rb~i2Iy<4`i%8tORvC>eiSHaniKzbfU>uFzaN@)}psN`6mb{ zIur1A^WID<;vaE-AZ}zBQEYvMPf8Eq_;2G(ry~E<_OWrGne#kbf=? zLV27ILUwER6tjxaxKSt6;yVZckKerF#MZ9;z;i`33xvdnTCQV(i(XK^*}9(b0x36x zOY@e!_q^$`<4Uz{$2HbV+{4<{0lvO4+}!VBPwqjR%4N9`L|^F7dK~QUn*f$YD2$#a zL9*p;|Kn~IhBDAUv3#qwo9juc20KQkcD;4T%NpA7PXuCsNe3r2(oIfq4TCxU!Qezv zK&$C3FejbJalgB7;eB`C8>#g$>|(nNkkn?1tgx)sDyr(<_k>|ug%h$ok%jtY2YH6y z-aLme{I-}e!0u%laoMrmK-01PbMq|tR3abBYI~GJpz9S11`ZoUssL;T8aN_gEC9}W z=+`hyd`p9-X@V}Q9AI=`+8FH8vQ7XTTZ6iS-vbY*YXM1-0swtuN>>}W%kJ`rboB%J z>jqd)0XH5CJN)3DaIC%oJp!?-4rh%Bcx|x&1rwO+Kl{SEc0w~fAAv@M;M{ZNVVMqm`1R*K`;|ok zDDsMh4TYgegGiD6*Wl&;%Xu7kwwrKhKd z2pdp`*^@f1b$VJOlRAbe`p3~Md7= zi0-hVD-LoI)qIXSC@uiig#g*@>4VNac=kG*+kfB=3RA?@Q8y02kI8T> zVe}QV{$;tbG!T5#F&TxFin4-SMH|P6xL(1sCPxU~K01MW;^*^GdcDzW@YEP5G_r(! z0p4;cSxZh>(d8N+jEVeSG>36>yvuF#4e~t}i|>vyHqaoq-a&&$EsyxTw|~~I3Zfu} zd|~f)k4Hei{BJBrdrbL4U7O{$uJ_Y}=WX1~v-TRuSnqxkA?JY0h&q~Q)^Zi5eb#54 z$SEJFS661erwLi0sL1cG2U<9p-H30-KP-+H>W^R1`~+>_8KW!5G}@uY0y>GQSV)+l zVHElYiX<9iz%(YBiXLp0ca{^bx~;9sFr&>3K&3p6ANK`Xz@+lnFDpH^#blX4$I}3PA?1=nU9}JzIu7{P_@UmI`#?P-Je* zCgTKaU;cc5TR%+7In}Qd?=$||&9B8;gp!dDD10siZ8MjH+}*(|Ta4cR_ha_&GHS=6 zMQ(K@!;UMTG9=N_)>MAZ(`WdZV})ez^`<0>G^XlAcuj?c+uY11Mw+E44J3djdqTN* zk;1Hcuw(%ec2xo9yP)Vp&Wj7ELw@tyF2*WLyu7`7QCIl&SCpk2S*S~Pj=1XqdzX7> z?V7A&jcQn_R~#9#efQ?>Iph|za2ynGRfls7Wt%2!BVWlEFpeMQ)#8Ea^;)Nyg$PD? zx?Is+v{QO7TWCRE^H_)lB|!oi4`l;Q)sCZzLVWzuJ@2BIg)9e`PJA%|JaOBxvStaw z6#ZZStxUM?NY1-#*X)ocl(Z=jNy8pwJD^5hsHD?3@F&CrCY8LQB3eJ0NuXK&cc*k$ zH{(0Y*bO_;a{f2yO=f>r{P|lYv?ZoCyG6bbqvif^qf~~kPB!kFL9{N9aC{X+i5uxO zKAU4ClTl|KyZE(#q&VeD|J_|M|Hx_7L<7~3I{h2264y9rMLEfmjK-(oxF^|IDA~HQ zAd2vJxk%T9BovFUxo;DAqvJth>Qq!;p&)x)Po=>xl6Pq;7hg=D)IO* zZ*&G48-}9WtNt|Z{hp$%I7;QGLvFatF)KxrS{&55%tPjzPgKG??wFOVzc4}y{NWOgf!ckLe1Y%JE}-Cto#r6))?^En$_ zj%h0Lbq*kIRMW%Z8ESp0*c1jb_{W}NHL;J$WH*~4;&hJ}(xH+~M zMmrHv{4AF3=JsNuzYag8*V*BuNI{8mYT0iB0}5MEKJ~Su;?IS~k4_cPJC%TuCm2sn z{(t`R4B^9i?JEbN73ilcznOqI zAEdn?cABI2+fS0vATrUtMJ$P@VZvgbBKJp$l@zh`&g7}x&4J;hLRf*G@)FfZer3=) z`tmVa|0|Vh2kT!WW+2nkEMc@4ep|lYt53alzp4QhM;9hO!s_%OzDg<#84nej<@3G1 z1~J0uJ6(D-xsKcf^)O+_SZl+iXZ0vInu&pCsi!xrFzqO7uxwt-jwb3$l`^TKdyiJ* z*q=>xFVA<5NSS-jybDB>=##pfmaZQIvvoE2$>#72)C+lm1=iWUeP!?1GDG)F5{}(^hbB!w(TA)kk$pM z{`5)@n^P8d!nydGpP1a4JCG>It{$h4kr?S@@lC*#XmBPL&F32K4xrM0VZ^*^Uir&l zQt#!bBXLx!Ul@A-8m%Y4l+NRb!K)Cm;)aB@VZbh$&!o$L5*=L}y$(~Pev-pVQLFy^ z`{j?iMP_+ANN#&O5eQ2WeF1VY?=xCfR);CxI$lGiWDq`ZWjPARM3HIe??zGG+UydR z@nrvffLuBrb1mJ7U}RD<;1J1+SEFp++qa+bR@*w6;NS@CYS1oOZs3B3uc1jQd5;0p zk^QH2iZ`0k!S`MyeP$xAwNadB6$R&mr&~bLURH5F$h7TyRCoXPQ#6vil5>gDpX$K| zXri%t!1wLD0KrL0hCO!U)mOx3Jb_q#SjpJ3xLw0vurJ|j6{#YM6W--up#%|Pk{25P zQ{|QBX~G#nW~7^A>R|m-i|;wo^^p__fu|Pt`>~jc>c8Ls%;ZA(F4gz`t)qU79jwyQ z6WL5@J?wPOhx2Ut%AVRf$@vpz0DmHqW5n^;tt-WocF)9OK(cxcMHVUMfez#o96bQ; zYg)KKAyktlbVoEz!*^#%6EpV+l60V0bgWH_y8^YSNT}t$@SD(Dxz)%$3Ep=t%hJ_U z)W8{V$<21k;`M4FY)0pk?G>bzls6b3km3-YYIoz>@@*T@*KRkX7(!Is#h9~nqM$}L zZ(u|^)k2=A73acfdGWHV(~&v3gf*FJ@rCq4cO3!nsBnb9WriEWxo)Cm;;Bm}eoz-l z2*YEe`z;+|?(!|9X(Y&3E{E~El{%;F1&Qa(r(*sVb*t~iF3e+VKsr|ouiF$S$z*)* zdUdd@sH(t_SDni5<=*1I=k2ieWoLTPT|a+fcNT3lF~}LF`x5u@f%_Ed9z4TGWgI*$ zBk;Q*|5%Ln%~HCM`_@gH9D7=1C0&>d4S(wkua)R#`0!W6M~ONSc47%XHz}7AH+43> z3#xpb$q+mYrms3dAvaq`5FM-*cILTu3r+|IH>QF4+ALOFW>`?Q^UuB7?iU1Bgz=>W zzs7f2X;drl&E@B!2pX-X=qrg)P+FxY812SWian~$4AsC|f}Uz7zTM&YS39xq`1dbo z6FX=!_BVK%P1FYC&KJBheXJeqbhLf&D;jDAEyqMi83j>l%W`At2>G}0}kgfKJf%z}|m_OPd| zo~23qw$-)!=eVSJye}=bRgXB&S>EA^dr{IWs>C04y`=PUEvicrv(>b~I6zWU6Yn`+ zb$fJ1d|7B5cRWm|mpN;s2GDNc0aQl!A5VXE?k3c<4z@p=zMm+F6Jb&BqYB2rWA|kKAn{o8p z*th?$%%$p;MS&>fI09AYgVp8Ad!0TN(q6K+BABsJzjw0hXlX$p zz26Dir*X{hygRfafB#zxkX45QqTvBU(VbqKmS@OkkYfs=M11*N27u7UXLp93_}oBnp`pzRsXE!V~xUiC{AJVtSWgkK( zzwpG__YFjJ^w*-`zAyY>j3$)pD{jo3Je8cH|Gt;f zNMR>~3sDHLD#$O96o30vLLgbxveF6!ny6AFjf(r&k@yTp=`9e$ww1jox~Iw|dV=;? z!&>`s72m~5O5TSI+Rs*W1C6nEAKX-`@He!oP2;xFO3grJUm`#{Y@-JJ%OBTMgUgZl9+_d*DQ!|EV2AA?7(Z4;e(fqJ9Yn<1 zIUO3nj!kjc=XnlePjuK}S}}U(t7zJv@#SgND%trxgqg~bD8JbdEAmq>qxZn>h4bMb zDBqX%09AT4IR2=(-jCU9iNr`%ho(hmT9xE{z;%&H+FyDKN~byykfR2VKgB(V2F)^`7ijNgV-DRZkWFz}-Ix^By6;`T- zb-7^Y$S*1ieoh#Z`bsndem3W8$Oj4L!uSBUzYhHkJ7wRpdDHWTU;gtBd7z*Nd)fa`8PW+5p5e6osXwFNpp>Mz0?S`I`zXn zBHVO+-5pLM^KEk@GZs_kEK@k<@6-N4>V1(RS69z&?JHtQ)y0+7>N)q8+5}RS{D@Xc z{2Q%dG9Qck)vH+t1acBU~R<5M7mqfh+F`PmP0FKZCPz-(y zut#*}y7pjlF1~bWecz<<8XEm4eOBAyOm6}XR|j_aFI{{chdgys zm7=<6bV`VoMt~Bmb73SZE^Bm}2z1#AMObnG3X9J(Kn+9`@OoURQ9o4-(bY8yLk~IO zrnFC>t+o!0_;pqwknPy(*ngJoyDSk_8G6RysO9r+Rbrc7v>q&?!V{~^Ih@Jr}!fbt;`b^v&bL%BX>uhtx552P`_ zc;Yb88(fLByLI-{q$g*T9u!GKfYm#sSk0_UU|fk&AthOc@bnrmgxhNBET&SI1qwhhoNw6hOO1}L>2i> zWIH!l@Hs*L>=zaV&>IDY9ZO9$koZt zu1XZG3F_gNX{PL{nqB{n_yRSe`8fNlb0qYQvq zjuj#H!e!9B1!EZw0o&)NN5EDH;If|kH46KoBG<3){9XKIEz{fOnv!}iCA~~D@vcwn zF8+>P1QE|Wd0l$-EC{Ox zLiKd^b~X@+$7Bu;vSI!?UXU<2F&ggEzvH{!N=9IneLG5!u`d-#P_~lP=r6ZshxICN(ZczA6RNtGP1<~gNjgDcE5YM)QH^b?^yu1cn z^tkJf$6t8uH=s21-Kl{2ba`0@Xh2^O;Rq;%h5%RS%Sx61#U@PUw_)k{C~}DBG>$2T zeepy&?4|@@Zb>poOIRSeROyZ9OilB>E1Y@47?JL8=2WM>l^wd{`HU=uB%u>U;6!=t z35<94fRFLQj=0!+-_cABjEURgpp^-tbAi_kr0a^|1;5atKnMF9*BRv^?LSYnzG9Ql z+V}?+Xkydr>k29=DqbDHVgvH=YJDC(AQRi8923145U@BTL_}c0ZQgeQc>&l1Z!XXl zeE?7vLoiAL^EsZ6zT_8%3uWtu4UD}FDtO9)CImbMVb8J@;}Thgj@!nObL4x8%mGXZ zhIhoW68Uj7E`~p!^&fPYdp{8y8qUoOCZ5ue1j`}rlI_)-Nu0tg2Tu-seT{(Kl*fTD z&%cZHba{7ZQh4(0bujagt>B#|0gx{fzGo3roh^Zjqyw&2)XVmc04@yjSV2@Z#cPE~?T0sn7KjPio9Exu|EDB{be)iMR z7rA+l+Oro1%1z>5ypOyK0{9K`K~UY^Ogw!`swi4KPAzOTyR;M*csD*l712N4k}!S@ z$mc23W87gANYnvKCk2^M;-9x7uZ=F|n7nRGNP1Y2+pp~2+usv?IX#?J0K&pb*T5XD zEX=q(vs_wfD2n}NSJW%aH7o~AL-w1nGTa!-r#58}J7~YXR>{_7PW`@u56D(bc zEX$AfDQtUhLmsS&$$#2m^jXZPW?8=KND!VNy92O!c4H*uZE6#YEw$mM3W@w*3iJM- zzuF>VX0syE;z(n`WDEQHVtb%ffk13mn{^>P_?s*Mk`#$8Z!l}$flHm(2N0>Vbzo{= zzaHAZ0C}#CfKVi~flCo24DI(4FJ4hmL6+T-mIolSn%X#_CEI3fpEP6{5!{KZkwB-) z#(l6l{sX2MS#qRLM@`QQ24gZ|aE}ZJ5MAt$6_pJXvczz{?YpxfM{4pKYlU%v;AX28 zej=Tz za0B&D#!|!P8}~jLIf+q3`F0zTQsKV6)iLGV4Ou@Xmi7hV#Tu_F3zGQbOnw~RP} zMc3#20W^z%A%g>za)zL~P;{Fz4Uy-)A2nsYm)KA|fK!b|7j75OXh@ zcf&52EVnI0(4Kh_FoAT9N?buI9s z;q?^9qU=rOam!M;%!rD!pIp38a)19jacPk07hRD$93yzn`Fc{24dHw$q7_)KQX=}_=g{NEqrFBxWS>~$dx*c z(I7D7#XbV&_WzlShhsy%bb>rCtzIr%2CtK5y%n}ahJ$!n(Zj1`26sI@cJk-;i*bGc zI9TiEn%Bkrh9Am)`V6&DWwKdh>P2N=T5DqblBEm~M%#M2IMUPXj)XMh>QYFwLxqXG z&%c*CC{bjL$uI(?pUUIrb8nm@WwG*1pE-Til*5J|KXC_`HYoLr*Ld^2E2&K#I;nS* z>YoL1SfE^dfkau(6!cXq^A_&yPw%)d9VIdr>wZ~lb7!M2Q>3i|Eibp3*?K=df3k|S zhMLCD%i=(+4C>ZGa9pmaV6IuT(e2?~fuQaB)GgFFO#gA};Tg2{Gg-z1QXV+N6w`MS zhMJc2?NJ}7g0Smin{7$Nb%;;a7V#!BzTR4tvK6|ZFn2C1(4@Hg8FonB(sY+CPTWWp znrKmHX%svU3cT?~u(TnNYjB`MXsql9tzDZ0*3;MCxD!rN8U}SV*BYzEU|5W$i`xDB zAuHlDzvdh%$>bj=c>py)zD)b}O=)jDQ%B_>?iI%HD=gz)8*ZJ1=wu?T6?$arNk4hy zgRg3#MEIo@l}XNFjk3f|DhVdL9;}tiBQ&!d}Hj!>2m`-tW%IQ&^tlP z{GB$^hMWH@v`P={ze_O(8pOkwT$AIU*K1%91vV*%7c?5E!MN<-^2&33w$e^Djn(fL z(_J(fdx=m6#tNXZ2TGFybK;kPGLZ7Xs6Lo039_oDayVHqBx3QB_U~V1Y3zQfp~E>} zphN8$?u1VNUXI%Fz89s-{Sc=u?1O4MTg@|; znH>>LWsC?kndUnwQdswVJkD-!8{ndil!~#)URMz%lx2n$MV4Z&U3_Sx|IYDkWK(@* za0Nv~c{a0ps|8K`O(Sz9VYD<09?{H{?03G1Jemqw=f$Hw7YrFGoM@Da@ZE&GHf$N$ zqOV-Dm6}!rqjYCjO0OLS#`5T{!JO9Oo^uP*T(x`Gk|(hu)Z9)>yBRz^ie&8Y`#&gy zzeWE`l!3FXTWBsj5^N+r$WdOR^O_mBh6zAhVeOR?$^UI)JGN;c2vg_+nS1wk!FMU} zV!qU&!<$~bHOzaLd8OPeU|+c@pZLL%_~Ff0vZN{&f}75P>M~lq<9p3yp^aNjbJa#% zQrW;jbHo1$VX(tE{r@5iq%LItcnI(52N2P^C$xMBn7xxIpO`!KjrlrDLp-4OV>zAm7g@v4} zYL?6j%LiLN%#_h_EW z$i`j%VFIWS)l?Q)OY%8g+^Ixk$Bf}#M@wvp#E&I>uZo_s{BMu}w_NR>_aPC#Wpn6% zgA7`Ew#+SY`+>Q3*HI2XDjL;tUP)j^nsI0k0fg^-*z8Wo-RrE{}Oqzzwh2PMv^I!Ri zz{mS*r_+rt0&@iie>3^fRE`i>1QeuOeLUrmvk=kTH_VO6r7`C1sKQR#ZUhSksgYIaBwZ!U@e!vqiIC0o)`VZg8{s_M4H+gDfvfuD8 zu8aQ*G4RY3nhAsboKFRvuyiolIYVg$krFI(pKy_-lL{&GZ&5ZT4;K+(iEpCRqbMpT zt^SDAV|abMno=+XT226gLl}@g9nOOV)f;ShC4Y3x-dj%~&T#QKH z-xMPa2Xq@u?|P~_InTVSR4ipj`cZS?k)usC9ORgdHjL8Yg*LWYiS;*(WX|kor@=1w z%E<4q8ILS)h7M>at`WQqGLL5o3{oI{Cs(Dd*=pr#oPN*iYv05rlnOo@sLifwk&&^X zfoCu|DfAQ~YlV=QdXhghoP+Ku?7)=L=0t?nxW9| z)&$>guAc2HND#-6lW-1eKSSh=<&`<>eK0qbHBFeDAuDBiD%U z=!kfbGA7Z8*tc%a^P<{&G{|u9_v9kKlKXp(>v_^k`0hQRtH*c}B)%%Z&fbq|q7j-E zjmF-7!~DP}rpj|&)`TlOfbmmNo_#QtylG(2$tA~@#1m`Y3{Zy-HOoih>_mP8N8FQ#HUa#9U3&-mazu19 zCWZ%iTX-e7zueQ6(|fpG9G=|t3#>CjBm}AR`>t74Q99=FiQZD3+$x#@vfEs*)|XpI{x;MP@&Fl4Uuu2d{)%mzd+pLxq=pyex^upNKNu`=u%u zWaXjWGWFL&9%sG6-L~mwxZlLd$`qSxs|g9sz@4{7Gjg@rVj~7Ij-t^$KQK_3m%^{; zTq2hS2M^U^Z;wCy@{|i$?+C{9nL<`k!Zb)0U}H`3!&fqX?a*Tr$5igx*;oY_ zli8bn`g(7_FqV;M6#JJuJ?YgxbDmKU@~VK^<(;&c42G_J;jGohncIdVUrstNOL}hS z(babmUBVWZKUamJacdFB`2NE3(bXIL96p>*C%PGopRbl z{2T|HNioWct9Em{n0g4D zY8PEj3pQ`s^ietGf=aAT-P-m9W4c~Rmy(P8vN9iuC@EHyW5k+_mjMqdf&-q(C;>sO z2Dg%?R>}9tl?7=9BAAJe&6oR0_|fa=8t9HAx9h!XDmx*2rMQAwgF<7N1y3ok4|Mn8 zZ6f3%&|j+iw$FxfaOpPYsi>WcxdLF(#QI^0wQsiht_#IS`v`d8!Ua#lDh9HfEN^b% zecJ`=-O)%TW+nFDE9|sZFCWFGiaCplew=sCPFZ;$p@5?uRDjQeL*zH|hZ8H|bGT6+ zCvr;|j~xkS;1yeSDC_ph>Fbzm&&>c0)MkspxSh8A;F-118=5sKG#mc4>tXbL$hW~# z7w3uxj)B^@As&5R7Pu1mJ= zvf)F7R+_qf0mw!1@G*bl>LbE~MXGcD-JG$0C53eNAer#H9GLo>x#ug0{=T5BALQ@* znXB0F<8^o>CMvGGOAON6iE;;pSXd`sD-uR_%xd#ogyn8UUOnc7aLudDx&msEm}OXO z0xT_YXJ<}bTU-o&%B`+@pTIu5r@o!%sZG{mV_E?__ygXt6C!c0Nzn|3y|bl({w+~U zIzVwu297O=OwW(mvyHk|=0ArZpM;iyK}1_S4YeY&efx&8MBqGjWdD&1+dz0R+R^t{ zAYI8o=udyqdk||Y8gUbcq~_wnHCJh{PCh^26!W!iKNBt*N-1gVGzBW;kFhozU7Cqj zWr^|NMpue}3xUsd?{<^#o%4#93b%ZwK-6=i3qzLAIo-oIt{152krVus8qC^Iq=$<);JhIBk3A%P8K9(2rqr+ot`&ojDTIpS`?BmZLPOHlWy zbwqe1J5xrw7UUob1_Kj=b^Ga_b*n%9bSYQtcj^>uV~p>kYyuBACKS-N>Y9Vb?_N7%hs*irj@WZF=Yd=4^i*(=hh!zu6bcF-A|)!MI%O=+ zB%SD!bC8=uO!bm;)_2sn?xR2Ol2e_`tQQ_1j}f60vE4x%!sYr`^WVWN!_FfL5no;3 zdU!+xa5h&`kdt?p>^Od8!u30CMiu88JN3Zo`;^3nIOxz*KfhHOfoswm^*(3k{-l~t zkOcL^sh=72j~}`ACv~2u$Ip*Y%#H6yKlOo(vJm4j%t)um_TVvo%3!o02i;By4Wux(=x`sPOy5sIQl#rbYxHMg%4RYF-9o4q_; zq$g&S6XE**Uc60F@7JpIeb}uaH2H^o1+u9DWo3+_5)v=rfIzTLCoLo6?Wn$U+XlQW zpZ%O)^^mWT0v`*)op>_BLmqG;ZHb35@Aq*^imEie@IUCjIBN3f}fCNPy1_AF^ zM{X!Xto!gXx2*J;>AA683C zN`%$ak_?19)o+Wi#Qeu>1LwDcZ0RwcE!Vu-F4kHXQabQs#y#*D0226y&1907ckx7? zy|N7hw&Hh)+DSzpmq@VTE`RA%rJcdU<`A#j<}O@vALl6+I#lF>#_X>P_E&_qEb%|! zJSWMM;Gf4yCXc*=J1&0clI$0un6aND(lx|<9hh*kNBIK9cAaDB$USbKX z#zvSt3+y@hB&wtc+7YXl>qpsjJkBA!8raQWl1^O#s1RU%M4o`$+bqC5h(`Vb*(0&3 zsbOHsc4z-}B9#LH_yrLBaQefqi7^_RLjM&ICN(Ky_~Pcw+`1Av98dJ@^Uxc6Zp;oe zL;``97B72TV5Xxg^lpu+n#r$H?oS>Z5)<)eBdm-JZ{J&3m^=l*H92-dIiJjx_sO?E z?Y#p@**#nH`aRkD-q;KpmHj6q?!6dDYO>Q1hUDbYg+^rD`(-WE9|q418}@i%x0o~v zh9+y&5zVolN^Jz)QOI^#JE$S==$8~&Z6uESqj+5QI8C+0wLSO<#xr~V%oKXJQ8*5W zy}?w{`qtlTASc@4qp=b5oQ?NA@t3OGifx8~_Z@=ZT);PdVer>C2c?zFydw2xBRHn! z=HygVaJ3c_{h-GL-N~+;pP#=_V-~Sq6wpQ>t*+A48@HA52O0{vUjq1#n|i{%ct&8a zSHNWJ@H(Z9B~IeC{|0OhQf?v6Y%y%klsA%^Gp7TyC+Ubj!(n^zcPJ_V%08!(nQHg; z{C6~iFp|tN@X+j}qxsNLq3N$vEAd^zgYW5dC&6teY{0?kO3r?Ps4Dg2bbkT0O3y^7 zi=J(xkTb3^ap+qt+rdYE0H6;Z9Hd2hmmCyA?12VGO%g>VzU^j?Y++Kd5wh@n#2+ZP zoUgv!urM-;nwnCYYW0sTE-u`Ty;sLjW^A6eFs`RuBYVnR-R%4>1goT`7ufF2GO+gZ z-sd0g-@U&!4sRCG@C#w| z4GwDR{w>*k-jd(HKdrcJM}6kgcta;f{!wq9DQT|(>HI)+k!6hmZxC$>=@VpECbGs% z?lV(z$0N77;8hA-R5q^({L=*BBLI@bMoCv-`p@t-h^nf(@m*apl;5vJtQDvERkzPm zfJ| z8+|^&BM*x{`#es5$mh2u5hQ-cy<@!o*+Xd^Y`Y?+N=JN;or$R357(oXvhuEr?lvaM%z9xn5+-1wtqFf9?obMd<}gkAF1?R@sHCW_~2nG;v$Fq=8Ztl z(e{AWb)~m1A+z-h9BOZ}t^SnZn zU!NN{$96}~=BZ>rNo93-aC*gmvT;nOf@a7538pi1d%$F1-oWJO{^Un>@zhpiG9{uM zbzw8(2+owA4x@kr-l~1iB`phsgnKiYpU3lKM;FLSg1rAijFlcx{AE38D1pWV=pLAm z0y~tPi}1WXuDb!&|2r)=0DHZ4xxAE=krA=9q+{-ALk*8Wy_XHWJQNg`mZm>HKd<_7 zqHoO=Pl?#kv`4_G-||XMP99ji-z4~cKxAqES7Tr`x7ib7jyq1?(+cQDQrR(!$U1X^JLS8}1Lz=b9gu101ebsr{O^p6$kkS4-kb1n$1=TI z95aiN*y*4Az@B@(@Y4bnybLtd{JQ;zWM-YZdop9%f`uSB)`oSy}!Q zg3l`w+5!tt$F0};gdaSxKqRw>jWd$@s9Wd$lYra0_sth6MD;S=u&-ZVLC$VYlw7YE z!H4YHb?kCSd`CZ}DHV_POY*D)Z#P*~fWBJ`z1EVo=S4FB`kilU7)?Nw0kn?6CjsSk z<|*2<6?Gl>;^R<=ao+ilLywPof{W(0_q}EDl-s>S2`RUoflS<40xtCO*#c3(MGOr! z*M3b5yqh8X12J}Sk?D`WO?VXDCP@b_2xK$#E8nCB@jHeRW7lu zgL(QidXieW9RFAq)#2M0)*^*`^get^Zr zG%hsn;(WA(eCFW5<914#Ay0F4v>uZqQp;#Z&)bZ7&TSj8cbVH>fdaY>wb8egdaZHE zX#psRL{Lx~sQ6%ecspg<4rMW(nDf^LA8NzRl5TwVJt-Sm*WF_SOGjPg-0_F!z#aJ^ zp=^ook5p7HqJa5Q<)A{$S&8f7QonE|cHs{Ceu#9%bb}+yujFPFm6ZG+5l{>PLLqqB z7cs==DN@Z^T>4*$D2yQUIa=fhA{2n4hOl%zU@(7s3%IB^8-b{bYHHa3GHabV7d;6i z$LaGX*Ema5(J(K1jCWTDez1SiP7Av@a^)w!Kk5mMiuzpb0Gd)Oo)aH5>&=-wu73N+ z2;9D6fm~;-NAEN1@oleJs+@h>9YHQojF~?VFl}~u!2BZEr^sZ{0}n(qxJ^$jhiuND zaI8v7k*R~PjzB2AspXSpTJU-Jo8Ob0q0WGpm@(OiWi$FbL(B9bxOth1l-*YKcV6+x@0Zxag;;hy~4-7 z;BGrq0v7)f7onl0?6m4PX0dz3YMZnceY|^r9+BZc7nhWrFOHkn<`fjbBieLrMKH1D zPJ0RukArI=U&uik07;1>*To5uw|~X&9_xdgl#@=w*7h~vdak@Dxm?FV%b1*x+C|h@ z7zTsS`J=KjMt?LxPzG|d+e2|7t9>VwE{Eys@x-K@wl+fSq@<#vGEp-#s^RghzNwRF zA+jgy)yabdQ$#+bT@$8Per8qYk5@#`oi6iF7+P9d3)LnFRwdSGpn^;{4xBOD$>Zy% z0c$*`reuj732XhHgB|Mh^Hk%Yq|Css_uU4I-4k|_T9RaF%mAohRn_|97;En}nSd8XXnvYKF4AV%pRxk6$GoBhVe zo&EhE4GlOoj6Xo4ISwUxY(gi)*Nmg@<-yX7(s2X*`-a~y8l^Bc&YCvuLAQphNmm|> zf8qZWTk_~@hx;Ba1=a3if8zNv?gkC|zcz{25#bRI7e!OCrajXG1ASyGkl)!~>#0-1rbu}8Cmq#Kj<4}!xDTkHNkLk$C zVZhNOG$zIllhfa6Z02dOCy|oQzB2nPUK+=)g>tBErqayS1@d>fGx4=#n1k>O0z zSIz0C+=S1IwBG^!U1zb%yvgW-l4j$vNST`_6}5bdAMXBG;K-4%krA1rz^c9eJw(+D zkjv!7gt{!|)=_Y9j0e>M;}R~4*Vnxob~#oa59#pIF5i|t|F#nt+MnwOws8P}y29~& z-hXef9n%O2Y--|Z`*}e0;Bijb2JscJIOE2UsHwEp zJL(;&s8<|lvV4zuKf;%kl$>>i94hK)L)1${8kRqsj#%rtEZoy+Z#d@64hQu_eNW7GB*DX=SfTkBKt?);^-zmGJ(#TOx9-Y z?b>DS@a2kOXbCe23Zq2C$HxaCLxl0=?Hna#qK4Ac0PQu zWT&Kr4(c=W-69?&p|%E4;^>~I%W9|*g_DcSJIlBPZw=M%T2xz&5tMnKd|1iIj0(e~ zP8rP;_h^#DQja>`0L^LttdddZ%=%H{kVa3uG%Lm~sX;2(yaj{wFR*+0c zDKALmQ0_KnfM$E$D0WhFqtp4$&;_^ZKn%%s@n45j#_?wCPYbU(%^R1&nPcQAz}E4* zwRWvdUB%&zr#`WxqtjHrlLa_kNMyUgJTuezAE=>Y6TzjADSqVE#3P(^K_-q_j+8@Q zG;h3J^n0_Qq@)*>mq!U2k@xvBh%wo7zZYw1KuShcL1!NeLv#MJJ~bsB}0 zus{toTc{S56_Y~4?|sJ!Dy=dndmci8S-}!x4+yagZ%a_lv`Y`yG$%3!Qj&3?n@T_d z+iPeiSQ!}wQhL5OH1ZjoDCTy_{cJwcG^i>XSv!qU&m-|P6#BaAHC)g{ZYYDz{bHnG_Fw%=M)?^Dzm$O%yva{t=5>OoPCsBqW@ze!(> zPP_9^Rn_RqGi!n}q4MGU9UXQg6yIHNpe)I(A|LGedPmBh%HS#2A@{v8_V)w)pP@TR zu^~chaD|Uip`leRkGB!@S5;vpOm(O}0=kwd`x&p_bmkws;|*T$IX|uFS&6CUHiv`PiyCgfrGBSPCf(N6>cuJjzzwwlf!Zv0?6=O?NOQf2BHBubRF2o=wpueZClMwm7%3l7oJQ{29zgn zLw{_AYIwX(h6{2aEu4P~rdR1>s*q$VQ896$*+{}fBO?mDzM|mQDrBjZHo<)^LfFK7 zloZVKTw@r5m-R}Fbu)0=UrDq7>>&z=Y@YSRC{i2fL?sSvSF1Z8sO4uMm;YX=%|ZL> z6gd+PcC=6KT7=DGF8|u}D-|ky#C<6CgTg&H?fdcGzi>&@??ZMY}So325cB4wdb z#S^c_lv2Cm;tG}3I!D{HOUp$>^j6`x$n<-)e2oMVgt=Aympp{M7jL974@T&-bgG=N zO9i`1gmNAP5+55~ZJ!ztU7sK5{=xHQ-Mp8Z@wy}I*)IHGY-~LF)AI{uI_Ez0A|_S| zBWruQ)8(Z-Hz%opfB>n-rEz5oC(h$-6v@~H@A@|Z$kT<$45#y-X)4pedy%L)oUDt- z@|$!+^Jw$~RD14rI)vDOzDDb|mKHXPfpJcT3rWn}&4t~)&pR9dg^$a-Riua8}k5iW&9oq06 zL?9epy~bsJb-x@WJK12%5PzC^;(glKAv2VYnNWdiVubh)2_`5ahrss_`99m@*pT8i z(IBnvZ%AQS3 zG_Ss!{8sCddC%%88%fIt$prhaHtgy^!`_=m_rk8@;*MtAVUE{k@2b=0=XmYOCez-! lODwmXhI<|2ZXr$H81yu@SwX8A(L;eBDKUA`3Sq;5{{m2%*!lnf literal 40256 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 { +// if (game.settings.get('mess', 'modify-rolling')) +// Hooks.on('getChatLogEntryContext', (html, options) => { +// options.forEach(e => e.condition = false) +// return []; +// }) +// }) \ No newline at end of file diff --git a/src/scripts/modify-rolling.js b/src/scripts/modify-rolling.js index cc6f780..2331591 100644 --- a/src/scripts/modify-rolling.js +++ b/src/scripts/modify-rolling.js @@ -52,6 +52,15 @@ async function createControls() { game.settings.set('mess', `${game.userId}.autoroll-selector`, data); }) }); + + const controls = document.getElementById('chat-controls'); + if (controls) + controls.insertBefore(div, controls.childNodes[0]); + else + Hooks.on('renderChatLog', (app, html, data) => { + const controls = document.getElementById('chat-controls'); + controls.insertBefore(div, controls.childNodes[0]); + }) return div; } @@ -68,62 +77,6 @@ async function onChatCardAction (ev) { } async function getToHitData({actor, item}) { - if (!item.hasAttack) return null; - const actorData = actor.data.data; - const itemData = item.data.data; - const flags = actor.data.flags.dnd5e || {}; - - let rollData = item.getRollData(); - - // Define Roll bonuses - const parts = [`@mod`]; - if ( (item.data.type !== "weapon") || itemData.proficient ) { - parts.push("@prof"); - } - rollData.parts = parts; - - // Expanded weapon critical threshold - if (( item.data.type === "weapon" ) && flags.weaponCriticalThreshold) { - rollData.critical = parseInt(flags.weaponCriticalThreshold); - } - - // Elven Accuracy - if ( ["weapon", "spell"].includes(item.data.type) ) { - if (flags.elvenAccuracy && ["dex", "int", "wis", "cha"].includes(item.abilityMod)) { - rollData.elvenAccuracy = true; - } - } - - // Apply Halfling Lucky - if ( flags.halflingLucky ) rollData.halflingLucky = true; - - // Attack Bonus - const actorBonus = actorData.bonuses[itemData.actionType] || {}; - if ( itemData.attackBonus || actorBonus.attack ) { - // parts.push("@atk"); - rollData["atk"] = [itemData.attackBonus, actorBonus.attack].filterJoin(" + "); - if (!isNaN(Number(rollData["atk"]))) { - parts.push("@atk"); - } - } - - let roll = new Roll(rollData.parts.join('+'), rollData); - rollData.totalModifier = roll._safeEval(roll.formula); - rollData.totalModifier = rollData.totalModifier >= 0 ? '+' + rollData.totalModifier : rollData.totalModifier; - if (rollData["atk"] && !roll._formula.includes('@atk')) { - rollData.parts.push("@atk"); - roll = new Roll(rollData.parts.join('+'), rollData); - rollData.totalModifier += `+${rollData["atk"]}`; - } - const situationalModifier = document.getElementById('mess-roll-mod'); - if (situationalModifier.value) { - rollData.parts.push(situationalModifier.value); - roll = new Roll(rollData.parts.join('+'), rollData); - rollData.totalModifier += `+${situationalModifier.value}`; - } - rollData.formula = roll.formula; - rollData.terms = roll._formula; - return rollData; } async function getDmgsData({actor, item, spellLevel = null}) { @@ -697,15 +650,17 @@ export default async function modifyRolling() { // possible that this function g ets called *after* chatLog creation, so check if its there already, and if yes work with the existing one. // this needs further investigating - Hooks.on('renderChatLog', async (app, html, data) => { - const div = await createControls(); - const controls = html[0].querySelector('#chat-controls'); - controls.insertBefore(div, controls.childNodes[0]); - }); - - // roundabout way to get the listener do what *I* want... - // Since adding my own listener in renderChatLog was "to early". - CONFIG.Item.entityClass.chatListeners = function (html) { + // Hooks.on('renderChatLog', async (app, html, data) => { + // const div = await createControls(); + // const controls = html[0].querySelector('#chat-controls'); + // controls.insertBefore(div, controls.childNodes[0]); + // }); + // moved the whole script into ready to try to remove a few problems happening with loading it, etc. + createControls(); + + // Since all this was moved to the ready hook i have to write my own listeners for stuff + const chatListeners = function(){ + const html = $(document.getElementById('chat-log')); html.on('click', '.card-buttons button', onChatCardAction.bind(this)); html.on('click', '.item-name', this._onChatCardToggleContent.bind(this)); @@ -717,6 +672,7 @@ export default async function modifyRolling() { html.on('click', '.mess-button-to-hit', rollHit); html.on('click', '.mess-button-dmg', rollDmg); } + chatListeners.bind(CONFIG.Item.entityClass)(); Hooks.on('preCreateChatMessage', async (data) => { const div = document.createElement('div'); @@ -730,9 +686,8 @@ export default async function modifyRolling() { }); - Hooks.on('renderItemSheet', itemHook) Hooks.on('renderActorSheet', actorSheetHook) changeAbilityTemplate(); -} +} \ No newline at end of file diff --git a/src/scripts/modify-templates.js b/src/scripts/modify-templates.js index e5beea9..8091392 100644 --- a/src/scripts/modify-templates.js +++ b/src/scripts/modify-templates.js @@ -1,4 +1,4 @@ -export default function changeTemplateFill() { +export function changeTemplateFill() { // #MonkeyPatchingFTW // better than stealing the code, replacing one line and then release it under a/the wrong license.. @@ -60,4 +60,104 @@ export default function changeTemplateFill() { Hooks.on('renderMeasuredTemplateConfig', (app, html, data) => { html[0].querySelector('.file-picker').dataset.type = 'imagevideo' }); +} + +export async function dndTemplateSettings() { + if (game.system.id !== 'dnd5e') return; + + Hooks.on('renderItemSheet', itemHook); + + const importedJS = (await import(/* webpackIgnore: true */ '/systems/dnd5e/module/pixi/ability-template.js')) + const AbilityTemplate = importedJS.default || importedJS.AbilityTemplate; + + + const _originalFromItem = AbilityTemplate.fromItem; + AbilityTemplate.fromItem = function(item) { + const template = _originalFromItem.bind(this)(item); + + // generate a texture based on the items dmg type, ... + // Add settings to define custom templates for stuff. + let path = item.getFlag('mess', 'templateTexture'); + if (!path && item.hasDamage) { + const settings = game.settings.get('mess', 'templateTexture') || {}; + path = settings[item.data.data.damage.parts[0][1]] || {}; + path = path[template.data.t]; + } + if (path) + loadTexture(path).then(tex => { + template.texture = tex; + template.data.texture = path; + template.refresh(); + }) + template.item = item; + return template; + } + + // rather ugly, maybe find a better way at some point :shrug: + const origPrevListeners = AbilityTemplate.prototype.activatePreviewListeners.toString(); + const newFun = origPrevListeners.replace(/this\.refresh\(\)\;/, + // get targets + `this.refresh(); + this.getTargets(this); + `); + + AbilityTemplate.prototype.getTargets = getTargets; + AbilityTemplate.prototype.isTokenInside = isTokenInside; + + AbilityTemplate.prototype.activatePreviewListeners = Function(`"use strict"; return ( function ${newFun} )`)(); +} + + +function isTokenInside(token) { + const grid = canvas.scene.data.grid, + templatePos = {x: this.data.x, y: this.data.y}; + // Check for center of each square the token uses. + // e.g. for large tokens all 4 squares + const startX = token.width >= 1 ? 0.5 : token.width / 2; + const startY = token.height >= 1 ? 0.5 : token.height / 2; + for (let x = startX; x < token.width; x++) { + for (let y = startY; y < token.height; y++) { + const currGrid = { + x: token.x + x * grid - templatePos.x, + y: token.y + y * grid - templatePos.y + }; + const contains = this.shape.contains(currGrid.x, currGrid.y); + if (contains) return true; + } + } + return false; +} + +function getTargets() { + const tokens = canvas.scene.getEmbeddedCollection('Token'); + let targets = []; + + for (const token of tokens) + if (this.isTokenInside(token)) { targets.push(token._id); } + game.user.updateTokenTargets(targets); +} + +async function itemHook(app, html) { + const div = document.createElement('div'); + div.classList.add('form-group'); + div.appendChild(document.createElement('label')).innerText = 'Template Texture'; + const formField = div.appendChild(document.createElement('div')); + formField.classList.add('form-fields'); + const inp = formField.appendChild(document.createElement('input')); + inp.dataset.dtype = 'String'; + inp.type = 'text'; + inp.name = 'flags.mess.templateTexture'; + inp.value = app.object.getFlag('mess', 'templateTexture') || ""; + + formField.insertAdjacentHTML('beforeend', ` + + `); + const button = formField.querySelector('button'); + button.style.flex = '0'; + app._activateFilePicker(button); + const target = html[0].querySelector('[name="data.target.units"]'); + if (target) + target.closest('.form-group').after(div); } \ No newline at end of file diff --git a/src/scripts/rolls/apply-dmg.js b/src/scripts/rolls/apply-dmg.js new file mode 100644 index 0000000..33995ea --- /dev/null +++ b/src/scripts/rolls/apply-dmg.js @@ -0,0 +1,11 @@ +export default function modifyApplyDmg() { + Hooks.on('getChatLogEntryContext', function (html, options) { + setProperty(game, 'mess.chatLogEntryContextOptions', options); + + // Modify existing applies to only work on default rolls and not mess-rolls + const canApply = li => canvas.tokens.controlled.length + && li.find(".dice-roll .dice-result").length; + for (let i = 1; i < options.length; i++) + options[i].condition = canApply; + }); +} \ No newline at end of file diff --git a/src/scripts/rolls/controls.js b/src/scripts/rolls/controls.js new file mode 100644 index 0000000..b132a57 --- /dev/null +++ b/src/scripts/rolls/controls.js @@ -0,0 +1,76 @@ +export default function() { + + Hooks.on('renderChatLog', chatLogHook); + + registerSettings(); +}; + +function registerSettings() { + game.settings.register('mess', `${game.userId}.adv-selector`, { + name: 'Mess - Advantage Selector', + default: 'normal', + type: String, + scope: 'user' + }); + game.settings.register('mess', `${game.userId}.autoroll-selector`, { + name: 'Mess - Autoroll Selector', + default: {hit: false, dmg: false}, + type: Object, + scope: 'user' + }); +} + +async function chatLogHook(app, html, data) { + const div = document.createElement('div'); + div.classList.add('mess-roll-control'); + + const advSelector = game.settings.get('mess', `${game.userId}.adv-selector`); + const autoRollSelector = game.settings.get('mess', `${game.userId}.autoroll-selector`); + const templateData = { + advantage: advSelector === 'advantage', + normal: advSelector === 'normal', + disadvantage: advSelector === 'disadvantage', + ...autoRollSelector + } + + div.insertAdjacentHTML('afterbegin', await renderTemplate('modules/mess/templates/roll-control.html', templateData)); + + div.querySelectorAll('.mess-adv-selector a').forEach(e => { + e.addEventListener('click', async function(ev) { + ev.preventDefault(); + ev.stopPropagation(); + const arr = Array.from(ev.currentTarget.parentNode.querySelectorAll('a')); + const currIdx = arr.findIndex(e => e.classList.contains('mess-selected')); + arr[currIdx].classList.remove('mess-selected'); + const newSelected = arr[(currIdx + 1) % arr.length]; + newSelected.classList.add('mess-selected'); + game.settings.set('mess', `${game.userId}.adv-selector`, newSelected.name); + }); + // Toggle in the opposite direction with right click + e.addEventListener('contextmenu', async function(ev) { + ev.preventDefault(); + ev.stopPropagation(); + const arr = Array.from(ev.currentTarget.parentNode.querySelectorAll('a')); + const currIdx = arr.findIndex(e => e.classList.contains('mess-selected')); + arr[currIdx].classList.remove('mess-selected'); + const newSelected = arr[(currIdx + arr.length - 1) % arr.length]; // add length cause javascripts modulo is strange + newSelected.classList.add('mess-selected'); + game.settings.set('mess', `${game.userId}.adv-selector`, newSelected.name); + }) + }); + div.querySelectorAll('.mess-autoroll-selector a') .forEach(e => { + e.addEventListener('click', async function(ev) { + ev.preventDefault(); + ev.stopPropagation(); + + + ev.currentTarget.classList.toggle('mess-selected'); + let data = game.settings.get('mess', `${game.userId}.autoroll-selector`); + data[ev.currentTarget.name] = ev.currentTarget.classList.contains('mess-selected'); + game.settings.set('mess', `${game.userId}.autoroll-selector`, data); + }) + }); + + const controls = document.getElementById('chat-controls'); + controls.insertBefore(div, controls.childNodes[0]); +} \ No newline at end of file diff --git a/src/scripts/rolls/dice.js b/src/scripts/rolls/dice.js new file mode 100644 index 0000000..aa8c7b6 --- /dev/null +++ b/src/scripts/rolls/dice.js @@ -0,0 +1,302 @@ +function getD20Modifier() { + return document.getElementById('mess-roll-mod').value; +} + +function getAdvantageSettings() { + return game.settings.get('mess', `${game.userId}.adv-selector`); +} + +export async function rollD20(data) { + let adv = getAdvantageSettings(); + // Determine the d20 roll and modifiers + let nd = 1; + let mods = data.halflingLucky ? "r=1" : ""; + + // Handle advantage + if ( adv === "advantage" ) { + nd = data.elvenAccuracy ? 3 : 2; + mods += "kh"; + data.title += ` (${game.i18n.localize("DND5E.Advantage")})`; + } + + // Handle disadvantage + else if ( adv === "disadvantage" ) { + nd = 2; + mods += "kl"; + data.title += ` (${game.i18n.localize("DND5E.Disadvantage")})`; + } + + // Include the d20 roll + let diceFormula = `${nd}d20${mods}`; + if (data.reliableTalent) diceFormula = `{${nd}d20${mods},10}kh`; + data.parts.unshift(diceFormula); + + const d20Mod = getD20Modifier(); + if (d20Mod) + data.parts.push(d20Mod); + + let r = new Roll(data.parts.join('+'), data); + r.roll(); + const d20 = r.parts[0].total; + let templateData = {...data, + tooltip: await r.getTooltip(), + roll: r, + crit: d20 >= 20, + fumble: d20 <= 1 + } + + const template = await renderTemplate('modules/mess/templates/roll-card.html', templateData); + + let chatData = { + user: game.user._id, + type: CONST.CHAT_MESSAGE_TYPES.OTHER, + content: template, + speaker: { + actor: this, + alias: this.name + } + }; + let rollMode = game.settings.get("core", "rollMode"); + if ( ["gmroll", "blindroll"].includes(rollMode) ) chatData["whisper"] = ChatMessage.getWhisperIDs("GM"); + if ( rollMode === "blindroll" ) chatData["blind"] = true; + + ChatMessage.create(chatData); +} + +/** + * Extracts the data needed for an attack roll. + * @param {ActorEntity} actor + * @param {ItemEntity} item + */ +export async function getToHitData({actor, item}) { + if (!item.hasAttack) return null; + const actorData = actor.data.data; + const itemData = item.data.data; + const flags = actor.data.flags.dnd5e || {}; + + let rollData = item.getRollData(); + + // Define Roll bonuses + const parts = [`@mod`]; + if ( (item.data.type !== "weapon") || itemData.proficient ) { + parts.push("@prof"); + } + rollData.parts = parts; + + // Expanded weapon critical threshold + if (( item.data.type === "weapon" ) && flags.weaponCriticalThreshold) { + rollData.critical = parseInt(flags.weaponCriticalThreshold); + } + + // Elven Accuracy + if ( ["weapon", "spell"].includes(item.data.type) ) { + if (flags.elvenAccuracy && ["dex", "int", "wis", "cha"].includes(item.abilityMod)) { + rollData.elvenAccuracy = true; + } + } + + // Apply Halfling Lucky + if ( flags.halflingLucky ) rollData.halflingLucky = true; + + // Attack Bonus + const actorBonus = actorData.bonuses[itemData.actionType] || {}; + if ( itemData.attackBonus || actorBonus.attack ) { + // parts.push("@atk"); + rollData["atk"] = [itemData.attackBonus, actorBonus.attack].filterJoin(" + "); + if (!isNaN(Number(rollData["atk"]))) { + parts.push("@atk"); + } + } + + let roll = new Roll(rollData.parts.join('+'), rollData); + rollData.totalModifier = roll._safeEval(roll.formula); + rollData.totalModifier = rollData.totalModifier >= 0 ? '+' + rollData.totalModifier : rollData.totalModifier; + if (rollData["atk"] && !roll._formula.includes('@atk')) { + rollData.parts.push("@atk"); + roll = new Roll(rollData.parts.join('+'), rollData); + rollData.totalModifier += `+${rollData["atk"]}`; + } + + const situationalModifier = getD20Modifier(); + if (situationalModifier) { + rollData.parts.push(situationalModifier); + roll = new Roll(rollData.parts.join('+'), rollData); + rollData.totalModifier += `+${situationalModifier}`; + } + rollData.formula = roll.formula; + rollData.terms = roll._formula; + return rollData; +} + +/** + * Rolls the to hit roll and updates the html target. Also updates the message if found. + * @param {Click Event} ev event targetting the button which initiated a real or virtual click event + */ +export async function rollToHit(ev) { + // Extract card data + const button = ev.currentTarget; + button.disabled = true; + const card = button.closest(".chat-card"); + const messageId = card.closest(".message").dataset.messageId; + // Check if user owns chat message, else return + if (messageId) { + const message = game.messages.get(messageId); + if (!(message.owner || message.isAuthor)) { + ui.notifications.error('You do not own the permissions to make that roll!'); + return; + } + } + // Get the Actor from a synthetic Token + const actor = CONFIG.Item.entityClass._getChatCardActor(card); + if (!actor.owner) return false; + + // Get the Item + const item = actor.getOwnedItem(card.dataset.itemId); + if ( !item ) { + return ui.notifications.error(`The requested item ${card.dataset.itemId} no longer exists on Actor ${actor.name}`) + } + + let rollData = await getToHitData({actor, item}); + let adv = getAdvantageSettings(); + // Determine the d20 roll and modifiers + let nd = 1; + let mods = rollData.halflingLucky ? "r=1" : ""; + + // Handle advantage + if ( adv === "advantage" ) { + nd = rollData.elvenAccuracy ? 3 : 2; + mods += "kh"; + } + + // Handle disadvantage + else if ( adv === "disadvantage" ) { + nd = 2; + mods += "kl"; + } + + // Include the d20 roll + rollData.parts.unshift(`${nd}d20${mods}`); + + let r = new Roll(rollData.parts.join('+'), rollData); + r.roll(); + let div = document.createElement('div'); + div.title = `${rollData.parts[0]}+${rollData.terms} = ${r.formula} = ${r.total}. Click to see rolls.`; + div.classList.add('dice-roll'); + div.classList.add('mess-dice-result'); + const span = div.appendChild(document.createElement('span')); + span.innerText = r.total; + div.insertAdjacentHTML('beforeend', await r.getTooltip()); + const tooltip = div.childNodes[1]; + tooltip.classList.add('hidden'); + const crit = rollData.critical || 20; + const fumble = rollData.fumble || 1; + + const d20 = r.parts[0].total; + if (d20 >= crit) { + span.classList.add('crit'); + card.querySelector('.mess-chat-dmg .mess-chat-roll-type').innerHTML += ' - Crit!' + card.querySelectorAll('.mess-button-dmg').forEach((e, idx) => { + const formula = e.dataset.formula; + const r = new Roll(formula); + r.alter(0, 2); + e.innerHTML = ` ${r.formula}` + e.dataset.formula = r.formula; + }); + } + if (d20 <= fumble) + span.classList.add('fumble'); + + ev.currentTarget.parentNode.replaceChild(div, ev.currentTarget); + if (messageId) { + const message = game.messages.get(messageId); + message.update({content: card.parentNode.innerHTML}); + } +} + +export async function getDmgData({actor, item, spellLevel = null}) { + if (!item.hasDamage) return null; + const actorData = actor.data.data; + const itemData = item.data.data; + let rollData = item.getRollData(); + + if ( spellLevel ) rollData.item.level = spellLevel; + + rollData.parts = duplicate(itemData.damage.parts); + if (itemData.damage.versatile) + rollData.parts.splice(1, 0, [itemData.damage.versatile, "versatile"]); + + if (item.data.type === 'spell') { + if (itemData.scaling.mode === 'cantrip') { + let newDmgPart = [rollData.parts[0][0]]; + const lvl = actor.data.type === 'character' ? actorData.details.level : actorData.details.spellLevel; + item._scaleCantripDamage(newDmgPart, lvl, itemData.scaling.formula); + rollData.parts[0][0] = newDmgPart[0]; + } else if (spellLevel && (itemData.scaling.mode === 'level') && itemData.scaling.formula ) { + let newDmgPart = []; + item._scaleSpellDamage(newDmgPart, itemData.level, spellLevel, itemData.scaling.formula) + if (newDmgPart.length > 0) { + newDmgPart.push('upcast dice'); + rollData.parts.push(newDmgPart); + } + } + } + + const actorBonus = actorData.bonuses[itemData.actionType] || {}; + if (actorBonus.damage && parseInt(actorBonus.damage ) !== 0) { + parts[0][0] += "+@dmg"; + rollData["dmg"] = actorBonus.damage; + } + + for (let part of rollData.parts) { + let roll = new Roll(part[0], rollData); + const dmgType = CONFIG.DND5E.damageTypes[part[1]]; + if (dmgType) + part[1] = game.i18n.localize('DND5E.Damage' + CONFIG.DND5E.damageTypes[part[1]]); + else if (part[1] === 'versatile') + part[1] = game.i18n.localize('DND5E.Versatile'); + part.push(roll.formula); + } + + return rollData; +} + +/** + * Rolls the dmg dice listed. If the event points towards an existing message the message will get updated + * @param {Event} ev + */ +export async function rollDmg(ev) { + // Extract card data + const button = ev.currentTarget; + button.disabled = true; + const card = button.closest(".chat-card"); + const messageId = card.closest(".message").dataset.messageId; + + // Check if user owns chat message, else return + if (messageId) { + const message = game.messages.get(messageId); + if (!(message.owner || message.isAuthor)) { + ui.notifications.error('You do not own the permissions to make that roll!'); + return; + } + } + const formula = button.dataset.formula; + + let r = new Roll(formula); + r.roll(); + let div = document.createElement('div'); + div.title = `${button.dataset.terms} = ${r.formula} = ${r.total}. Click to see rolls.`; + div.classList.add('dice-roll'); + div.classList.add('mess-dice-result'); + const span = div.appendChild(document.createElement('span')); + span.innerText = r.total; + div.insertAdjacentHTML('beforeend', await r.getTooltip()); + const tooltip = div.childNodes[1]; + tooltip.classList.add('hidden'); + + ev.currentTarget.parentNode.replaceChild(div, ev.currentTarget); + + if (messageId) { + const message = game.messages.get(messageId); + message.update({content: card.parentNode.innerHTML}); + } +} \ No newline at end of file diff --git a/src/scripts/rolls/index.js b/src/scripts/rolls/index.js new file mode 100644 index 0000000..2265f6a --- /dev/null +++ b/src/scripts/rolls/index.js @@ -0,0 +1,12 @@ +import rollConrolls from './controls.js'; +import applyDmg from './apply-dmg.js'; +import modifyRolling from './modify-rolling.js'; + +export function rolling() { + // Don't do this stuff if the settings are disabled. + if (!game.settings.get('mess', 'modify-rolling')) + return; + rollConrolls(); + applyDmg(); + modifyRolling() +} \ No newline at end of file diff --git a/src/scripts/rolls/modify-rolling.js b/src/scripts/rolls/modify-rolling.js new file mode 100644 index 0000000..137a2bf --- /dev/null +++ b/src/scripts/rolls/modify-rolling.js @@ -0,0 +1,314 @@ +import {rollD20, getToHitData, rollToHit, getDmgData, rollDmg} from './dice.js'; + + +export default function() { + setupHooks(); +} + +/** + * Initializes all the hoohks! + */ +function setupHooks() { + Hooks.on('preCreateChatMessage', preCreateChatMessageHook); + Hooks.on('renderActorSheet', actorSheetHook); + + // Bind my own chatListeners to the item class and execute them. + Hooks.on('ready', chatListeners.bind(CONFIG.Item.entityClass)); +} + +/** + * Makes sure one attack is rolled for every chat card that has a dmg or attack button. + * @param {Object} data chat message data + */ +async function preCreateChatMessageHook(data) { + const div = document.createElement('div'); + div.insertAdjacentHTML('afterbegin', data.content); + let btn = div.querySelector('button[data-action="attack"]'); + if (!btn) + btn = div.querySelector('button[data-action="damage"]'); + + if (btn) + renderAttack({currentTarget: btn}); +} + +/** + * Renders an attack chat card + * @param {Click Event} ev pointing towards the card that is supposed to initiate the event. + */ +async function renderAttack(ev) { + if (ev.type === 'click') { + ev.preventDefault(); + ev.stopPropagation(); + } + + // Extract card data + const button = ev.currentTarget; + button.disabled = true; + const card = button.closest(".chat-card"); + + // Get the Actor from a synthetic Token + const actor = CONFIG.Item.entityClass._getChatCardActor(card); + + if ( !actor || !actor.owner) return; + + // Get the Item + const item = actor.getOwnedItem(card.dataset.itemId); + if ( !item ) { + return ui.notifications.error(`The requested item ${card.dataset.itemId} no longer exists on Actor ${actor.name}`) + } + + let targets = game.user.targets; + // Don't roll for all targets if its an AoE, due to only rolling e.g. dmg once for all targets + // TODO: Maybe add target list or chat cards for making saving throws + // or not, since it would just spam the chatlog.. hmm + const areaSkill = Object.keys(CONFIG.DND5E.areaTargetTypes).includes(getProperty(item, 'data.data.target.type')); + if (!targets.size || areaSkill) + targets = [{data: { + name: "someone", + img: "" + } + }]; + const spellLevel = parseInt(card.dataset.spellLevel) || null; + + const template = 'modules/mess/templates/attack-card.html'; + + const attackData = { + actor, item, + toHit: await getToHitData({actor, item}), + dmgs: await getDmgData({actor, item, spellLevel}), + sceneId: canvas.scene.id, + user: game.user.id + } + + const autoroll = game.settings.get('mess', `${game.userId}.autoroll-selector`); + + let rollMode = game.settings.get("core", "rollMode"); + for (const target of targets) { + const allowed = await item._handleResourceConsumption({isCard: false, isAttack: true}); + const attackTemplateData = { + ...attackData, + target: target.data, + flavor: item.data.data.chatFlavor.replace(/\[target\.name\]/g, target.data.name), + allowed + }; + let html = await renderTemplate(template, attackTemplateData); + + + + if (autoroll.hit || autoroll.dmg) + html = await autoRoll(autoroll, html); + + + let chatData = { + user: game.user._id, + type: CONST.CHAT_MESSAGE_TYPES.OTHER, + content: html, + speaker: { + actor: item.actor._id, + token: item.actor.token, + alias: item.actor.name + } + }; + if ( ["gmroll", "blindroll"].includes(rollMode) ) chatData["whisper"] = ChatMessage.getWhisperIDs("GM"); + if ( rollMode === "blindroll" ) chatData["blind"] = true; + + ChatMessage.create(chatData); + } + + button.disabled = false; +} + +/** + * Autorolls hit or dmg, depending on which flag is set and replaces the template string. + * @param {Object} autoroll Defining whether to autoroll hit or dmg + * @param {String} template Defining the html template where the roll should happen. + */ +async function autoRoll(autoroll, template) { + let card = document.createElement('div'); + card.classList.add('message'); + card.insertAdjacentHTML('afterbegin', template); + if (autoroll.hit) { + let toHitBtn = card.querySelector('.mess-button-to-hit'); + if (toHitBtn) + await rollToHit({currentTarget: toHitBtn}); + } + + if (autoroll.dmg) { + const btns = Array.from(card.querySelectorAll('.mess-button-dmg')); + for (const btn of btns) + await rollDmg({currentTarget: btn}); + } + return card.innerHTML; +} + +/** + * Hook onto the Actor Sheet rendering, modifying the listeners for the roll ability and roll skill check events + * @param {*} app + * @param {*} html + * @param {*} data + */ +async function actorSheetHook(app, html, data) { + // TODO: Redo this with proper methods... this currently ignores the cool new modifier field + // maybe just ignore replace the abilitysave etc functions + const abilityMods = html[0].querySelectorAll('.ability-mod, .ability-name'); + $(abilityMods).off(); // find smth better here! + abilityMods.forEach(e => e.addEventListener('click', function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + + const abilityId = ev.currentTarget.closest('.ability').dataset.ability; + const label = CONFIG.DND5E.abilities[abilityId]; + const abl = app.object.data.data.abilities[abilityId]; + const parts = ["@mod"]; + const data = {mod: abl.mod}; + const feats = app.object.data.flags.dnd5e || {}; + + // Add feat-related proficiency bonuses + if ( feats.remarkableAthlete && DND5E.characterFlags.remarkableAthlete.abilities.includes(abilityId) ) { + parts.push("@proficiency"); + data.proficiency = Math.ceil(0.5 * this.data.data.attributes.prof); + } + else if ( feats.jackOfAllTrades ) { + parts.push("@proficiency"); + data.proficiency = Math.floor(0.5 * this.data.data.attributes.prof); + } + + // Add global actor bonus + let actorBonus = getProperty(app.object.data.data.bonuses, "abilities.check"); + if ( !!actorBonus ) { + parts.push("@checkBonus"); + data.checkBonus = actorBonus; + } + + data.parts = parts; + + data.title = game.i18n.format("DND5E.AbilityPromptTitle", {ability: label}); + + rollD20.bind(app.object)(data); + return true; + })); + const saveMods = html[0].querySelectorAll('.ability-save'); + saveMods.forEach(e => e.addEventListener('click', function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + const abilityId = ev.currentTarget.closest('.ability').dataset.ability; + const label = CONFIG.DND5E.abilities[abilityId]; + const abl = app.object.data.data.abilities[abilityId]; + const parts = ["@mod"]; + const data = {mod: abl.mod}; + + // Include proficiency bonus + if ( abl.prof > 0 ) { + parts.push("@prof"); + data.prof = abl.prof; + } + + // Include a global actor ability save bonus + const actorBonus = getProperty(app.object.data.data.bonuses, "abilities.save"); + if ( !!actorBonus ) { + parts.push("@saveBonus"); + data.saveBonus = actorBonus; + } + data.title = game.i18n.format("DND5E.SavePromptTitle", {ability: label}); + data.parts = parts; + rollD20.bind(app.object)(data); + })); + + const skills = html[0].querySelectorAll('.skill-name'); + $(skills).off(); + skills.forEach(e => e.addEventListener('click', function(ev) { + ev.stopPropagation(); + ev.preventDefault(); + const skillId = ev.currentTarget.closest('.skill').dataset.skill; + const skl = app.object.data.data.skills[skillId]; + + // Compose roll parts and data + const parts = ["@mod"]; + const data = {mod: skl.mod + skl.prof}; + if ( skl.bonus ) { + data["skillBonus"] = skl.bonus; + parts.push("@skillBonus"); + } + + // Reliable Talent applies to any skill check we have full or better proficiency in + const reliableTalent = (skl.value >= 1 && app.object.getFlag("dnd5e", "reliableTalent")); + data.parts = parts; + data.title = game.i18n.format("DND5E.SkillPromptTitle", {skill: CONFIG.DND5E.skills[skillId]}); + + rollD20.bind(app.object)(data); + return false; + })) +} + +/** + * My own chat listeners + */ +function chatListeners() { + const html = $(document.getElementById('chat-log')); + html.on('click', '.card-buttons button', onChatCardAction.bind(this)); + html.on('click', '.item-name', this._onChatCardToggleContent.bind(this)); + + // lets just use this for even more listeners + html.on('mouseenter', '.mess-chat-target', onMouseEnterTarget); + html.on('mouseleave', '.mess-chat-target', onMouseLeaveTarget); + html.on('dblclick', '.mess-chat-target', onDblClickTarget); + + html.on('click', '.mess-button-to-hit', rollToHit); + html.on('click', '.mess-button-dmg', rollDmg); +} + +// Only overwrite stuff for attack buttons +async function onChatCardAction (ev) { + if (ev.currentTarget.dataset.action === 'attack') + return renderAttack(ev); + if (ev.currentTarget.dataset.action === 'damage') + return renderAttack(ev); + if (ev.currentTarget.dataset.placeTemplate) + return renderTemplate(ev); + + return this._onChatCardAction(ev); +} + +/***************************************************** + * Mouse Listeners for the target img for chat cards * + *****************************************************/ + +async function onMouseEnterTarget(ev) { + ev.preventDefault(); + ev.stopPropagation(); + const token = await getTargetToken(ev); + if (!token) return false; + + token._onHoverIn(); +} + +async function onMouseLeaveTarget(ev) { + ev.preventDefault(); + ev.stopPropagation(); + const token = await getTargetToken(ev); + if (!token || !token.visible) return false; + + token._onHoverOut(); +} + +async function onDblClickTarget(ev) { + ev.preventDefault(); + ev.stopPropagation(); + const token = await getTargetToken(ev); + if (!token || !token.visible) return false; + + const pos = token.center; + canvas.animatePan({x: pos.x, y: pos.y}) +} + +async function getTargetToken(ev) { + const card = ev.currentTarget.closest('.mess-attack-card'); + const sceneId = card.dataset.sceneId; + if (sceneId !== canvas.scene.id) return false; + const tokenId = card.dataset.targetId; + if (!tokenId) return false; + + const token = canvas.tokens.placeables.find(e => e.id === tokenId); + if (!token) return false; + return token; +} \ No newline at end of file diff --git a/src/scripts/settings.js b/src/scripts/settings.js index 22fccf4..faed44a 100644 --- a/src/scripts/settings.js +++ b/src/scripts/settings.js @@ -8,7 +8,8 @@ export class MessSettings extends FormApplication { scope: "user", config: isDnD, default: isDnD, - type: Boolean + type: Boolean, + onChange: () => location.reload() }) game.settings.register('mess', 'better-draggable', { @@ -16,7 +17,8 @@ export class MessSettings extends FormApplication { scope: "user", config: false,// Change if implemented default: isDnD, - type: Boolean + type: Boolean, + onChange: () => location.reload() }) game.settings.register('mess', 'prepared-spell-tracker', { @@ -25,7 +27,8 @@ export class MessSettings extends FormApplication { scope: "user", config: isDnD, default: isDnD, - type: Boolean + type: Boolean, + onChange: () => location.reload() }) game.settings.register('mess', 'add-scrolling', { @@ -34,7 +37,8 @@ export class MessSettings extends FormApplication { scope: "user", config: isDnD, default: isDnD, - type: Boolean + type: Boolean, + onChange: () => location.reload() }); game.settings.register('mess', 'modify-rolling', { @@ -43,7 +47,8 @@ export class MessSettings extends FormApplication { scope: "world", config: isDnD, default: isDnD, - type: Boolean + type: Boolean, + onChange: () => location.reload() }); game.settings.register('mess', 'modify-templates', { @@ -52,7 +57,8 @@ export class MessSettings extends FormApplication { scope: "world", config: true, default: true, - type: Boolean + type: Boolean, + onChange: () => location.reload() }); game.settings.register('mess', 'change-placeables', { @@ -61,7 +67,8 @@ export class MessSettings extends FormApplication { scope: "world", config: true, default: true, - type: Boolean + type: Boolean, + onChange: () => location.reload() }); if (isDnD) game.settings.registerMenu('mess', 'templateTexture', { @@ -99,7 +106,7 @@ export class MessSettings extends FormApplication { getData() { let data = super.getData(); data.dmgTypes = CONFIG.DND5E.damageTypes; - data.templateTypes = CONFIG.templateTypes; + data.templateTypes = CONFIG.MeasuredTemplate.types; return data; } diff --git a/src/scripts/welcome-screen.js b/src/scripts/welcome-screen.js new file mode 100644 index 0000000..627fb27 --- /dev/null +++ b/src/scripts/welcome-screen.js @@ -0,0 +1,43 @@ +export default function renderWelcomeScreen() { + const module = game.modules.get("mess"); + const moduleId = module.id; + const title = module.data.title; + const moduleVersion = module.data.version; + game.settings.register(title, 'version', { + name: `${title} Version`, + default: "0.0.0", + type: String, + scope: 'world', + }); + const oldVersion = game.settings.get(title, "version"); + + if (!isNewerVersion(moduleVersion, oldVersion)) + return; + + class WelcomeScreen extends Application { + static get defaultOptions() { + const options = super.defaultOptions; + options.template = `modules/${moduleId}/templates/welcome-screen.html`; + options.resizable = true; + options.width = 450; + options.height = 600; + options.classes = ["welcome-screen"]; + options.title = `${title} - Welcome Screen`; + return options; + } + + activateListeners(html) { + super.activateListeners(html); + + html.find('.show-again').on('change', ev => { + let val = "0.0.0"; + if (ev.currentTarget.checked) + val = moduleVersion; + + game.settings.set(title, "version", val); + }) + } + } + + (new WelcomeScreen()).render(true); +} \ No newline at end of file diff --git a/src/templates/roll-card.html b/src/templates/roll-card.html index 4494e41..04c07b1 100644 --- a/src/templates/roll-card.html +++ b/src/templates/roll-card.html @@ -4,7 +4,7 @@

diff --git a/src/templates/welcome-screen.html b/src/templates/welcome-screen.html new file mode 100644 index 0000000..f3a13e9 --- /dev/null +++ b/src/templates/welcome-screen.html @@ -0,0 +1,41 @@ + + +

Thank you for using MESS!

+

Make sure to read the README, to learn about this modules features, since there are quite a few and they are not all connected.

+

Bugs? Issues?

+

Read and follow these instructions. +

Suggestions? Feature Requests? Feedback?

+

Currently planned features (and bugs) are on the GitHubs Issue Board. Please check the issues if your suggestion does already exist. If it does, give it a thumbsup, else create an issue for it and label it as suggestion.

+

The issue board linked above can also be used to keep track of current development.

+ +

Best regards,
Moerill

+

+

+

Want to support development? Click here

+

+

Changelog v0.4.0

+
    +
  • + Important: Automatic template textures for DnD 5e are now independent of the rolling mode! They will get activated when you activate the scaling and video textures for templates! +
  • +
  • (Hopefully) fixed the module sometimes not properly loading, by removing race conditions created due to dynamic script loading.
  • +
  • Improved the roll controls above the chat window-content +
      +
    • Changed the advantage toggle to now using dice to display the current roll mode
    • +
    • Added a situational modifier field, which gets applied to all D20 based rolls. (attack rolls, saving throws, skill and ability checks
    • +
    +
  • +
  • + Restructured a whole lot of the code, so its a little bit less of a mess... (Even though this is the modules name! :P ) +
  • +
  • + Removed the default 5e context menu for applying damage, since this resulted in unwanted and confusing behaviour. I will revisit this for the next patch and add some option to apply damage using the rolls. I'm just not sure currently as to how i want to tackle this. Feel free to add ideas to the issue. +
  • +
\ No newline at end of file