diff --git a/CHANGELOG.md b/CHANGELOG.md index a42f984..766eb4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# v0.10.0 + + # v0.9 * NEW FEATURE: Smooth drag animations in the sidebar directories and on most actor sheets! diff --git a/README.md b/README.md index 43a83ba..8a8c898 100644 --- a/README.md +++ b/README.md @@ -11,25 +11,40 @@ Just kidding! This module does not serve a more specific purpose, like most of m Settings allow to granularly dis-/enable features to your liking. *Important*: If you change the settings you need to refresh the page for all connected clients for the changes to take effect! (This may change in a future update, but currently it only works this way. Shouldn't be to bad, since you'd enable most features once and not change them mid session.) ## Current Feature List -- [System independent](#universal) - * [Template Changes](#template-changes) +- [Mess - Moerills enhancing super-suit(e)](#mess---moerills-enhancing-super-suite) + - [Current Feature List](#current-feature-list) +- [Important Information!](#important-information) + - [FVTT Version compatibility](#fvtt-version-compatibility) + - [Module compatiblity](#module-compatiblity) + - [Bug Reporting](#bug-reporting) + - [Atribution](#atribution) + - [Support the development](#support-the-development) + - [Licensing](#licensing) +- [Universal Features](#universal-features) + - [Template Changes](#template-changes) - [Scaling and animated template textures](#scaling-and-animated-template-textures) - - [Hiding template border and grid highlight](#hide-grid-highlight-and-border-for-textured-templates) - - [Auto targetting on template movement](#auto-targetting-on-template-move) - * [Miscellaneous](#miscellaneous) + - [Hide grid highlight and border for textured templates](#hide-grid-highlight-and-border-for-textured-templates) + - [Auto targetting on template move](#auto-targetting-on-template-move) + - [Miscellaneous](#miscellaneous) - [Momentum based preview snapping](#momentum-based-preview-snapping) - [Drag and Drop animations for sorting lists](#drag-and-drop-animations-for-sorting-lists) -- [DnD5e specific](#dnd5e-specific) - * [More streamlined rolling and targeting](#rolling-and-targeting-change) - - [Custom chat cards](#custom-attack-and-damage-roll-chat-cards) - - [Autoroll and advantage toggles](#autoroll-and-advantage-toggle) + - [Some important information and known issues for this feature](#some-important-information-and-known-issues-for-this-feature) +- [DnD5e specific features](#dnd5e-specific-features) + - [Rolling and targeting change](#rolling-and-targeting-change) + - [Custom attack and damage roll chat cards](#custom-attack-and-damage-roll-chat-cards) + - [Extended tooltip](#extended-tooltip) + - [GM only information](#gm-only-information) + - [Always show GM attack card to players, but rolls only on demand](#always-show-gm-attack-card-to-players-but-rolls-only-on-demand) + - [Custom flavor text](#custom-flavor-text) + - [reactive context menu for applying dmg](#reactive-context-menu-for-applying-dmg) + - [Autoroll and Advantage toggle](#autoroll-and-advantage-toggle) - [Use items to add bonus damage to other items](#use-items-to-add-bonus-damage-to-other-items) - - [Automatic Ability template texture](#ability-template-textures) - - [Automatic template targeting](#auto-targeting-with-ability-templates) - * [Actor sheet changes](#actor-sheet-changes) + - [Ability template textures](#ability-template-textures) + - [Auto targeting with ability templates](#auto-targeting-with-ability-templates) + - [Actor Sheet Changes](#actor-sheet-changes) - [Numerical scroller](#numerical-scroller) - - [Sort items alphabetically](#alphabetical-item-sort) - - [Prepared spell tracker](#prepared-spell-tracker) + - [Alphabetical item sort](#alphabetical-item-sort) + - [Prepared Spell Tracker](#prepared-spell-tracker) # Important Information! @@ -56,26 +71,25 @@ Go to the [GitHub's issue board](https://github.com/Moerill/Mess/issues) and che **I will only take a quick glance at half hearted bug reports or Discord mentions! Don't expect me to react there!** ## Atribution -Thanks to @bsleys for his continued support on helping to enhance this module with features! - -Thanks to @BrotherSharp for the japanese translation! - -Thanks to @NickEast for his ![foundry project creator](https://gitlab.com/foundry-projects/foundry-pc/create-foundry-project) which i'm using in a modified version for my building and publishing workflow. +Special thanks to: +* GitHub User @bsleys for his continued support on helping to enhance this module with features! +* Discord User @BrotherSharp for the japanese translation! +* Github User @rinnocento for the portuguese translation! +* @NickEast for his ![foundry project creator](https://gitlab.com/foundry-projects/foundry-pc/create-foundry-project) which i'm using in a modified version for my building and publishing workflow. This module would not be possible without the great work from Atropos on FoundryVTT and the [DnD5e System](https://gitlab.com/foundrynet/dnd5e) for FoundryVTT! Part of the code (especially the code for the rolls) is heavily based on the DnD5es code, which is licensed under GNU GPLv3. The templates used in the videos are from [Pierluigi Riminis Perfect Spell Pack 2 animated](https://marketplace.roll20.net/browse/set/3954/perfect-spells-pack-2-animated). +## Support the development +I'm doing this project mostly alone (with partial help of some wonderful people mentioned above) in my spare time and for free. +If you want to encourage me to keep doing this, i am happy about all kind of tokens of appreciation. (Like some nice words, recommending this project or even a small donation over at my [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FYZ294SP2JBGS&source=url)). + ## Licensing Mess is licensed under the [LGPL v3](https://github.com/Moerill/Mess/blob/master/LICENSE). This work is licensed under Foundry Virtual Tabletop [EULA - Limited License Agreement for module development](https://foundryvtt.com/article/license/). -## Support the development -Want to help me develop? Send a merge request on this gitlab or contact me on Discord (Moerill#7205). -Want to support me in another way? -Leave me some nice comments (e.g. on Discord), recommend this module to others and/or leave a donation over at my [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FYZ294SP2JBGS&source=url). - # Universal Features ## Template Changes @@ -110,9 +124,11 @@ To make dragging around more beautiful! Letting the placeable always snap, makes Adds animations for Drag and Drop animations to make them more appealing to the eye and more obvious where stuff will end up when dropped. #### Some important information and known issues for this feature 1) Its supposed to work with all sidebar directories, that support a drag and drop workflow to sort. + * Scene directory may look a bit odd (especially for scenes without thumbnails) 2) Many actor sheets are supported out of the box. Tested with: * DnD5e default and tidy5e sheets * Pf2e character sheet + * The container box may look a bit misleading.. 3) sorting to the end of a list/folder is kinda difficult at the moment. For sidebar directories dragging onto the folder itself puts it at the end. (Trying to fix this just introduced quite a few more issues and was not worth the effort. Also to my knowledge its the same for base FVTT dragging) # DnD5e specific features @@ -121,14 +137,34 @@ This is a big one and encompasses a variety of features. ### Custom attack and damage roll chat cards ![Attacking](img/attacking.gif) +(While this gif has not all the newest feature in, it does display the main features of the roll cards.) Default DnD5e does need way to many clicks, just to do a single attack. There do exist other approaches to handle this, like BetterRolls, but i am not a fan of those. This feature streamlines the process of attacking or using an item/feature/spell (from now on summarized together as ability). Each time you use an ability the default chat card gets created as well as an *attack card* for each target you selected (or just one if no target is selected or the ability is an AoE skill.). Example card: ![attack card example](img/attack-card-example.png) Hovering over the target in the card does highlight it on the map (if visible) and double clicking it pans it into view. If a crit is rolled the dmg formulas are automatically adjusted to respect it by using the double amount of dice. +#### Extended tooltip +The dice tooltip (clicking on a result) now shows extended information. (Modifiers, proficiency bonus, ...) + +#### GM only information +For all rolls some extra information about the target will be shown: +* Armor Class +* Damage resistances +* Damage immunities +* Damage vulnerabilities + +#### Always show GM attack card to players, but rolls only on demand +You can set to always ignore the roll mode set for the GM for attack cards only. This will always display basic information, like the attacker, chat flavor for the attack and the target (if existent). You then can decide to show your rolled attack or damage results to your players by clicking on the eye icon beside the section header. (Only the result will be shown to non GMs. Clicking on the result will not show extra information for players, like rolled dice, modifier, etc.) +This has the advantage of easier tracking the flow of battle, targets and damage received for player characters. Also the players then can easily apply the damage received using the context menu to their characters. + #### Custom flavor text The flavor text (in the example ``The cat swipes at Badger lazily with a clawed paw.``) is the chat flavor text specified for the item. If you want to display the targets name in it, use ``[target.name]`` inside the flavor text. -*Rollable Tables as Flavor*: You can also specify to modify the flavor (partially) by adding a rollable table. You can do so similarly as you specify other entities inside e.g. journals, by using ``@JournalEntry[name]`` or ``@JournalEntry[id]`` inside the chat flavor text. The module will automatically roll the table and replace the reference inside the flavor text. ``[target.name]`` will get replaced afterwards, so you can even use that inside your rollable tables. +*Rollable Tables as Flavor*: You can also specify to modify the flavor (partially) by adding a rollable table. You can do so similarly as you specify entity links everywhere else in FoundryVTT, by using ``@RollableTable[name]`` or ``@RollableTable[id]`` inside the chat flavor text. The module will automatically roll the table and replace the reference inside the flavor text. ``[target.name]`` will get replaced afterwards, so you can even use that inside your rollable tables. + +#### reactive context menu for applying dmg +![Contextmenu](img/context-menu.png) +You can right click on a rolled dmg roll to select whether to apply dmg to the target of the chat card (or all selected tokens if the card has no target). +Alternatively you can right click on the *Damage* header to automatically apply the sum of all rolled damage die. (Same rule as above for target) Also if versatile damage was rolled you can choose whether to use the versatile or the non versatile damage as main damage source. ### Autoroll and Advantage toggle ![Roll toggles](img/roll-toggles.png) diff --git a/dist/css/mess.css b/dist/css/mess.css index afec5ad..5580f60 100644 --- a/dist/css/mess.css +++ b/dist/css/mess.css @@ -3,16 +3,22 @@ } .dark-mode .mess-attack-card { --border-color: #444; + --border-button: #444; --font-sub-color: #9c9c9c; - --color-success: #18520b; - --color-fail: #aa0200; + --color-success: #26a30a; + --color-fail: #c40502; + --font-color: #ccc; } #chat { --highlight-color: red; } .mess-attack-card { --border-color: #FFF; + --border-button: #999; --font-sub-color: #33322e; + --color-success: #26a30a; + --color-fail: #c40502; + --font-color: #191813; } #chat-controls .mess-roll-control { height: 1.8em; @@ -100,97 +106,315 @@ padding: 0 3px; margin: 3px; } -.mess #chat-log button[data-action="attack"] ~ button[data-action="damage"], -.mess #chat-log button[data-action="versatile"] { +.mess button[data-action="attack"] ~ button[data-action="damage"], +.mess button[data-action="versatile"] { display: none; } -.mess #chat-log .mess-attack-card { +.mess .mess-attack-card { display: flex; flex-flow: row wrap; justify-content: space-between; align-items: flex-start; } -.mess #chat-log .mess-attack-card button, -.mess #chat-log .mess-attack-card .mess-dice-result { +.mess .mess-attack-card #context-menu { + max-width: 150px; + width: 150px; + min-width: 150px; + text-align: left; + left: calc(50% - 75px); +} +.mess .mess-attack-card #context-menu .mess-context-menu-header { + padding: 0.3em; + background: #aaa3; + cursor: initial; +} +.mess .mess-attack-card #context-menu .mess-context-menu-header i { + padding: 0 3px 0 0; + width: 1.3ch; +} +.mess .mess-attack-card #context-menu .context-item { + line-height: 1.5em; + padding: 0 1.5ch; + display: flex; + flex-flow: row-reverse nowrap; +} +.mess .mess-attack-card #context-menu .context-item span { + text-align: right; + flex: 1; + font-size: 1.2em; cursor: pointer; - background: rgba(0, 0, 0, 0.1); - border: 2px groove var(--border-color); - height: 1.5em; - line-height: 1.2em; - width: 70%; - border-radius: 0.3em; } -.mess #chat-log .mess-attack-card .mess-dice-result { +.mess .mess-attack-card #context-menu .context-item label { + cursor: pointer; + flex: 0; + color: #aaa; +} +.mess .mess-attack-card .mess-dice-result { height: auto; } -.mess #chat-log .mess-attack-card .mess-dice-result span { +.mess .mess-attack-card .mess-dice-result .mess-roll-container { font-weight: bold; font-size: 1.4em; + height: 0.8em; } -.mess #chat-log .mess-attack-card .mess-dice-result .crit { +.mess .mess-attack-card .mess-dice-result .mess-roll-container .mess-roll-total { + font-size: inherit; +} +.mess .mess-attack-card .mess-dice-result .crit { color: var(--color-success); - color: #26a30a; } -.mess #chat-log .mess-attack-card .mess-dice-result .fumble { +.mess .mess-attack-card .mess-dice-result .fumble { color: var(--color-fail); - color: #c40502; } -.mess #chat-log .mess-attack-card .dice-roll { +.mess .mess-attack-card .mess-dice-result #context-menu { + min-width: unset; +} +.mess .mess-button-to-hit, +.mess .mess-button-dmg, +.mess .mess-dice-result { + cursor: pointer; + background: rgba(0, 0, 0, 0.1); + border: 1px solid var(--border-button); + box-shadow: 0 0 2px #FFF inset; + height: 1.5em; + line-height: 1.2em; + width: 70%; + border-radius: 0.3em; +} +.mess .mess-hit, +.mess .mess-miss { + position: relative; +} +.mess .mess-hit:after, +.mess .mess-miss:after { + background-color: var(--font-color); + width: 1.2em; + height: 1.2em; + position: absolute; + right: 0; + content: ''; + display: inline-block; + filter: saturate(80%); +} +.mess .mess-hit.crit:after, +.mess .mess-miss.crit:after { + background-color: var(--color-success); +} +.mess .mess-hit.fumble:after, +.mess .mess-miss.fumble:after { + background-color: var(--color-fail); +} +.mess .mess-attack-card .dice-roll { background: rgba(0, 0, 0, 0.1); - border: 2px groove var(--border-color); - height: 1auto; + border: 1px solid var(--border-button); + box-shadow: 0 0 2px #FFF inset; + height: auto; line-height: 1.2em; width: 70%; } -.mess #chat-log .mess-attack-flavor { +.mess .mess-hit:after { + mask: url(../assets/shield-impact.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shield-impact.svg) no-repeat 50% 50%; + bottom: 0px; +} +.mess .mess-miss:after { + mask: url(../assets/shield-reflect.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shield-reflect.svg) no-repeat 50% 50%; + transform: scale(-1, 1); + bottom: -3px; +} +.mess .mess-attack-flavor { flex: 1 100%; border-top: 2px groove var(--border-color); border-bottom: 2px groove var(--border-color); padding: 0.2em 0; } -.mess #chat-log .mess-chat-error { +.mess .mess-chat-error { flex: 1 100%; border-bottom: 2px groove var(--border-color); padding: 0.2em 0; color: var(--color-fail); } -.mess #chat-log .mess-chat-target { +.mess .mess-chat-target { flex: 40%; + max-width: 40%; border-right: 2px groove var(--border-color); + display: block; } -.mess #chat-log .mess-chat-target span { +.mess .mess-chat-target .mess-target-name { text-overflow: ellipsis; - font-size: 0.8em; + white-space: nowrap; } -.mess #chat-log .mess-chat-target img { +.mess .mess-chat-target img { + z-index: 1; border: none; cursor: pointer; + height: 75px; + display: block; + margin: 0 auto; } -.mess #chat-log .mess-chat-target img:hover { +.mess .mess-chat-target img:hover { border: 1px solid var(--highlight-color); } -.mess #chat-log .mess-chat-rolls { +.mess .mess-chat-target .mess-icon { + position: relative; + min-width: 1.2em; + min-height: 1.2em; + display: inline-block; +} +.mess .mess-chat-target .mess-icon:before { + background-color: var(--font-color); + width: 1.8em; + height: 1.8em; + position: absolute; + right: 0; + content: ''; + display: inline-block; + filter: saturate(80%); +} +.mess .mess-chat-target .mess-icon.ac:before { + mask: url(../assets/shoulder-armor.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shoulder-armor.svg) no-repeat 50% 50%; +} +.mess .mess-chat-target .mess-icon.dam-res:before { + mask: url(../assets/shield-impact.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shield-impact.svg) no-repeat 50% 50%; + left: -0.3em; +} +.mess .mess-chat-target .mess-icon.dam-inv:before { + mask: url(../assets/shield-reflect.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shield-reflect.svg) no-repeat 50% 50%; + transform: scale(-1, 1); + bottom: -0.5em; + left: -0.4em; +} +.mess .mess-chat-target .mess-icon.dam-vuln:before { + mask: url(../assets/broken-shield.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/cracked-shield.svg) no-repeat 50% 50%; + left: -0.5em; + height: 1.5em; +} +.mess .mess-target-overlay { + display: flex; + flex-flow: column nowrap; + align-items: flex-end; + overflow: hidden; + text-overflow: ellipsis; +} +.mess .mess-target-overlay .mess-target-name { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; +} +.mess .mess-target-overlay .mess-target-overlay-container { + width: fit-content; + max-width: 100%; + display: flex; + flex-flow: column nowrap; +} +.mess .mess-chat-rolls { flex: 60%; display: block; } -.mess #chat-log .mess-chat-to-hit, -.mess #chat-log .mess-chat-dmg { +.mess .mess-chat-to-hit, +.mess .mess-chat-dmg { width: 100%; text-align: center; - display: flex; flex-flow: column; align-items: center; justify-content: flex-start; + display: flex; } -.mess #chat-log .mess-chat-sub-label { +.mess .mess-chat-sub-label { font-size: 0.8em; opacity: 0.8; color: var(--font-sub-color); position: relative; top: -0.3em; } -.mess #chat-log .mess-chat-roll-type { +.mess .mess-chat-roll-type { margin: 0.5em 0 0 0; + width: 100%; +} +.mess-is-gm .mess-gm-info { + display: block; + border-top: 1px solid #aaa; + width: 100%; + list-style-type: none; +} +.mess-is-gm .mess-gm-info label { + font-weight: bold; +} +.mess-is-gm .mess-gm-info li:not(:first-child) { + padding: 0.1em 0; + border-top: 1px dashed #aaa; +} +.mess-is-gm .mess-gm-info i { + font-size: 0.8em; +} +.mess-is-gm .mess-gm-rolls .mess-chat-to-hit, +.mess-is-gm .mess-gm-rolls .mess-chat-dmg { + display: flex; +} +.mess-is-gm .mess-gm-rolls .mess-show-btn { + display: block; +} +.mess-is-gm .mess-gm-rolls .mess-show-players .mess-button-dmg, +.mess-is-gm .mess-gm-rolls .mess-show-players .mess-button-to-hit { + display: block; +} +.mess-is-gm .mess-gm-rolls .mess-show-players .mess-button-dmg + label, +.mess-is-gm .mess-gm-rolls .mess-show-players .mess-button-to-hit + label { + display: block; +} +.mess-is-gm .mess-gm-rolls .mess-show-players .mess-show-btn { + opacity: 1; +} +.mess-is-gm .mess-gm-rolls .dice-tooltip { + max-width: 100%; + overflow: hidden; +} +.mess-is-gm .mess-gm-rolls .dice { + display: block; +} +.mess-gm-rolls .mess-chat-to-hit, +.mess-gm-rolls .mess-chat-dmg { + display: none; +} +.mess-gm-rolls .mess-show-players { + display: flex; +} +.mess-gm-rolls .mess-show-players .mess-dice-result { + display: flex; +} +.mess-gm-rolls .mess-show-players .mess-button-dmg, +.mess-gm-rolls .mess-show-players .mess-button-to-hit { + display: none; +} +.mess-gm-rolls .mess-show-players .mess-button-dmg + label, +.mess-gm-rolls .mess-show-players .mess-button-to-hit + label { + display: none; +} +.mess-gm-rolls .dice-tooltip { + max-width: 0; + overflow: hidden; +} +.mess-gm-rolls .dice { + display: none; +} +.mess-show-btn { + opacity: 0.5; + height: 0; + position: relative; + top: -1.25em; + align-self: flex-end; + display: none; +} +.mess-show-btn i:before { + content: "\f06e"; +} +.mess-gm-info { + display: none; } .dnd5e.actor .ability-mod, .dnd5e.actor .ability-save { diff --git a/dist/lang/de.json b/dist/lang/de.json index 4bf175d..7d250e1 100644 --- a/dist/lang/de.json +++ b/dist/lang/de.json @@ -74,6 +74,10 @@ "maxCrit": { "label": "Maximiere kritische Treffer.", "hint": "Ändert das Verhalten kritischer Schadenswürfe, indem der Wurf des kritischen Extrawürfel maximiert wird!" + }, + "mode": { + "activate": "GM Angriffskarte immer allen Spielern zeigen, egal welcher Würfelmodus gewählt wurde.", + "hint": "Wenn diese Option aktiviert ist werden Angriffskarten vom GM immer allen Spielern angezeigt, wobei nur grundlegende Informationen wie Angreifer und Ziel angezeigt werden. Der GM kann dann entscheiden gewürfelte Würfe den Spielern zu zeigen indem auf das Augensymbol auf der Angriffskarte geklickt wird. Hierbei werden nur die finalen Würfelergebnisse angezeigt und keine Details, wie geworfene Würfe oder Modifikatoren." } } }, diff --git a/dist/lang/en.json b/dist/lang/en.json index 2869147..64aadd0 100644 --- a/dist/lang/en.json +++ b/dist/lang/en.json @@ -5,7 +5,23 @@ "cardTargetTooltip": "Double click to pan to token, if its scene is currently viewed.", "chatCardResourceError": "There were not enough resources left for this attack!", "toHit": "To Hit", - "damage": "Damage" + "damage": "Damage", + "contextMenu": { + "dmg": "Damage", + "healing": "Healing", + "full": "Full", + "half": "Half", + "double": "Double", + "error": "You must select at least one token to apply the damage to!", + "applyTarget": "Apply to target", + "applySelected": "Apply to selected" + }, + "applyDamage": { + "sceneNotFound": "Scene for Target not found!", + "targetNotFound": "Target token not found!", + "targetNotOwner": "You are not allowed to modify target tokens HP!" + }, + "showToPlayers": "Toggle whether rolled dice results are shown to players. " }, "rollConfig": { "Advantage": "Roll with Advantage!", @@ -74,6 +90,10 @@ "maxCrit": { "label": "Use maximum crits.", "hint": "Changes behaviour of critical damage rolls to maximize the damage of the extra dice!" + }, + "mode": { + "activate": "Always display GM attack cards to all users, regardless of the roll mode.", + "hint": "This will always display the attack card with some basic information, like the attacker and the target to all players. The GM then can manually decide to show the rolled results on demand by clicking on the eye button next to the section titles on the card." } } }, diff --git a/dist/lang/ja.json b/dist/lang/ja.json index f672796..5e559b3 100644 --- a/dist/lang/ja.json +++ b/dist/lang/ja.json @@ -81,7 +81,11 @@ "placeables":{ "label": "オブジェクト操作の変更", "hint": "コマ、タイル、壁等のマウスで動かせるすべてのオブジェクトのドラッグ中の動作を変更します。マウスを早く動かしているときはマス目に沿わず、マイスを遅く動かしたときに沿うようになります。この機能によりオブジェクトのドラッグをやめる前からどこに沿っていくのかを予測することが可能です。" - } + }, + "draggableLists": { + "label": "フォルダ/アイテムドラッグ地点予測アニメーションを使用する", + "hint": "ドラッグ&ドロップを行うときにドロップした場所がどこになるのかをなめらかなアニメーションで表示します。" + } } }, "reloadReminder": { diff --git a/dist/lang/jp.json b/dist/lang/jp.json new file mode 100644 index 0000000..5e559b3 --- /dev/null +++ b/dist/lang/jp.json @@ -0,0 +1,97 @@ +{ + "MESS": { + "attackCard": { + "target": "対象", + "cardTargetTooltip": "ダブルクリックで現在のコマに注目します。", + "chatCardResourceError": "この攻撃を行うのに必要なリソースが足りません。", + "toHit": "ヒット", + "damage": "ダメージ" + }, + "rollConfig": { + "Advantage": "有利でロールする", + "Normal": "通常ロール(d20を1個)をする", + "Disadvantage": "不利でロールする(´・ω・`)", + "ModifierTitle": "d20用の状況修正値", + "ModifierPlaceholder": "修正値", + "Hit": "ヒット", + "Dmg": "ダメージ" + }, + "actorSheet": { + "preparedSpellTracker": "準備呪文" + }, + "itemSheet": { + "templateTexture": "範囲テンプレート画像", + "bonusDmg": "ダメージロールに追加ダメージを追加する" + }, + "FVTTSettings": { + "button": "このごちゃごちゃした設定メニューに整理を!", + "description": "Messモッド設定" + }, + "settings": { + "tabs": { + "info": "情報", + "templates": "範囲テンプレート", + "dnd5e": "DnD5e", + "misc": "その他" + }, + "DnD": { + "specifics": "D&D5版用設定" + }, + "templates": { + "alternateTexturesLabel": "範囲テンプレートに画像を置く", + "autotargetting": { + "label": "範囲テンプレートを動かしたときに内部にあるコマを自動的にターゲットする" + }, + "drawTemplateBorders": { + "label": "画像を配置した範囲テンプレートの境目とマス目を描画しない", + "hint": "この設定をオンにすることで画像(または動画)をおかれた範囲テンプレートの範囲の境目とマス目を表示しなくなります。マウスをホバーすると依然として表示されます。" + }, + "DnD": { + "textureInfo": { + "header": "デフォルトテンプレート画像", + "1": "アイテムに個別のテンプレート画像がおかれていない場合はダメージの種別によって自動的に画像を配置します。", + "2": "参照されるダメージ種別は最初のもののみです。" + } + } + }, + "dnd5e": { + "numericalScrolling": { + "label": "スクロール編集", + "hint": "数字のみを持つ入力フィールドをマウスのホイールでスクロールすると1ずつ上下に動かせるようになります。" + }, + "itemSort": { + "label": "アイテムソートボタン", + "hint": "キャラクターシート内部のアイテムをアルファベット順にソートするためのボタンを配置します。" + }, + "preparedSpellTracker": { + "label": "準備可能呪文トラッカー", + "hint": "キャラクターシートの呪文タブに現在準備可能な呪文を管理するための項目を追加します。これは自動的に計算されるものではなく、あくまでもプレイヤー本人の目安として使われるべきものです。" + }, + "rolling": { + "header": "ロールとターゲット方法を変更する", + "activate": "有効化", + "hint": "以下の設定は上記の有効化がオンになっているときにのみ適用されます。", + "maxCrit": { + "label": "クリティカルダメージ最大化", + "hint": "クリティカルの追加ダメージを必ずダイスの目の最大の値をとるように変更します。" + } + } + }, + "misc": { + "placeables":{ + "label": "オブジェクト操作の変更", + "hint": "コマ、タイル、壁等のマウスで動かせるすべてのオブジェクトのドラッグ中の動作を変更します。マウスを早く動かしているときはマス目に沿わず、マイスを遅く動かしたときに沿うようになります。この機能によりオブジェクトのドラッグをやめる前からどこに沿っていくのかを予測することが可能です。" + }, + "draggableLists": { + "label": "フォルダ/アイテムドラッグ地点予測アニメーションを使用する", + "hint": "ドラッグ&ドロップを行うときにドロップした場所がどこになるのかをなめらかなアニメーションで表示します。" + } + } + }, + "reloadReminder": { + "text": "変更を保存するにはGMおよびPLのクライアントを再起動してください。", + "yes": "OK、全員リロードしてくれ!", + "no": "いいや、自分でやるね。" + } + } +} \ No newline at end of file diff --git a/dist/lang/pt-BR.json b/dist/lang/pt-BR.json new file mode 100644 index 0000000..d5335cf --- /dev/null +++ b/dist/lang/pt-BR.json @@ -0,0 +1,97 @@ +{ + "MESS": { + "attackCard": { + "target": "Alvo", + "cardTargetTooltip": "Clique duas vezes para deslocar para o token, se a cena estiver exibida atualmente.", + "chatCardResourceError": "Não havia recursos suficientes para este ataque!", + "toHit": "Para Acertar", + "damage": "Dano" + }, + "rollConfig": { + "Advantage": "Role com Vantagem!", + "Normal": "Role com apenas 1d20!", + "Disadvantage": "Role com Desvantagem!! :(", + "ModifierTitle": "Modificador Situacional apra rolagens do d20.", + "ModifierPlaceholder": "modificador", + "Hit": "Acerto", + "Dmg": "Dano" + }, + "actorSheet": { + "preparedSpellTracker": "Magia Preparada" + }, + "itemSheet": { + "templateTexture": "Modelo de Textura", + "bonusDmg": "Adicionar um bonus as rolagens de dano?" + }, + "FVTTSettings": { + "button": "traga alguma ordem nessa bagunça!", + "description": "Menu de Configurassão do Mess." + }, + "settings": { + "tabs": { + "info": "Informação", + "templates": "Modelo", + "dnd5e": "DnD5e", + "misc": "Outros" + }, + "DnD": { + "specifics": "Opções especificas para DnD5e." + }, + "templates": { + "alternateTexturesLabel": "Ative a renderização alternativa da textura do modelo.", + "autotargetting": { + "label": "Ativa a seleção automática de tokens quando um modelo é movido." + }, + "drawTemplateBorders": { + "label": "Não desenhe bordas de modelo nem destaque grade para modelos texturizados.", + "hint": "Ativar isso remove somente as bordas e o destaque da grade dos modelos texturizados. Se você passar o mouse sobre o controle de modelos, o destaque e as bordas serão mostrados." + }, + "DnD": { + "textureInfo": { + "header": "Texturas de modelo padrão", + "1": "Permite especificar texturas padrão escolhidas para modelos de magias, se nenhuma estiver definida para o item.", + "2": "O tipo de dano do primeiro campo de dano do item é escolhido." + } + } + }, + "dnd5e": { + "numericalScrolling": { + "label": "Rolagem Numérica.", + "hint": "Permite rolar dentro dos campos de entrada numéricos para modificá-los." + }, + "itemSort": { + "label": "Botão Classificar itens.", + "hint": "Adiciona um botão para classificar itens automaticamente em ordem alfabética crescente." + }, + "preparedSpellTracker": { + "label": "Rastreador de Magias Preparadas.", + "hint": "Adiciona um rastreador à guia do livro de feitiços. Esse rastreador permite um rastreamento mais fácil do número máximo de feitiços permitidos. Isso não é calculado automaticamente, é mais um lembrete de quantos você tem permissão para ter." + }, + "rolling": { + "header": "Rolagem e Alvos alternativas", + "activate": "Ativar.", + "hint": "Todas as configurações a seguir funcionam apenas se Ativar estiver selecionado.", + "maxCrit": { + "label": "Usar Criticos Máximos", + "hint": "Altera o comportamento dos testes de dano crítico para maximizar o dano dos dados extras!" + } + } + }, + "misc": { + "placeables":{ + "label": "Ative as alterações dos objetos.", + "hint": "Altera a maneira como as visualizações do objeto são exibidas, quando arrastadas. Eles seguirão o mouse sem problemas. Somente quando você começar a desacelerar para colocá-los com precisão, eles serão mostrados no local exato (por exemplo, encaixados na grade) onde terminarão após a criação." + }, + "draggableLists": { + "label": "Ative animações de arrasto mais agradáveis na barra lateral (e listas de itens para muitas fichas de ator).", + "hint": "Adiciona animações ao fluxo de trabalho de arrastar e soltar para obter um melhor feedback visual do que está acontecendo e para ver corretamente onde um objeto terminará quando soltar." + } + } + }, + "reloadReminder": { + "text": "Atualize a página para você e todos os clientes para que a maioria das alterações entre em vigor.", + "yes": "OK! Atualize por mim.", + "no": "Não, Eu atualizarei manualmente depois." + } + } +} diff --git a/dist/mess.zip b/dist/mess.zip deleted file mode 100644 index 3fb49e5..0000000 Binary files a/dist/mess.zip and /dev/null differ diff --git a/dist/module.json b/dist/module.json index f422037..dd489f9 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.9.0", + "version": "0.10.0", "minimumCoreVersion": "0.6.0", "compatibleCoreVersion": "0.6.2", "author": "Moerill", @@ -26,10 +26,15 @@ { "lang": "ja", "name": "日本語", - "path": "lang/jp.json" + "path": "lang/ja.json" + }, + { + "lang": "pt-BR", + "name": "Português (Brasil)", + "path": "lang/pt-BR.json" } ], "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.9.0/mess.zip" + "download": "https://github.com/Moerill/mess/releases/download/v0.10.0/mess.zip" } \ No newline at end of file diff --git a/dist/scripts/index.js b/dist/scripts/index.js index cb27ce7..3a88dd3 100644 --- a/dist/scripts/index.js +++ b/dist/scripts/index.js @@ -1,62 +1,66 @@ -!function(t){function e(e){for(var s,n,r=e[0],i=e[1],o=0,c=[];o{const e=t.closest(".tab").dataset.tab,a=document.createElement("a");a.innerHTML='',a.classList.add("mess-sort-btn"),a.title=`Sort ${e} alphabetically.`,a.style.flex=0,a.style.margin="0 5px 0 0",a.addEventListener("click",t=>async function(t,e){const s=await fromUuid("Actor."+e);let a=t.map(t=>t.items||t.spells).flat();a.sort((function(t,e){return t.namee.name?1:0}));let n=[a.shift()],r=[];for(;a.length>0;n.push(a.shift()))r=SortingHelpers.performIntegerSort(a[0],{target:n[n.length-1],siblings:duplicate(n),sortBefore:!1});const i=r.map(t=>{let e=t.update;return e._id=t.target._id,e});s.updateEmbeddedEntity("OwnedItem",i)}(s[e],s.actor._id)),t.prepend(a)})}async function n(){Hooks.on("renderActorSheet",(t,e,s)=>{a(0,e[0],s)})}s.r(e),s.d(e,"default",(function(){return n}))},"./src/scripts/add-scrolling.js": +/*! exports provided: default */function(e,t,a){"use strict";async function s(e,t,a){if(!a.actor._id)return;t.querySelectorAll(".filter-list").forEach(e=>{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 i=r.map(e=>{let t=e.update;return t._id=e.target._id,t});a.updateEmbeddedEntity("OwnedItem",i)}(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(t,e,s){"use strict";async function a(){Hooks.on("renderActorSheet",(async function(t,e,s){e[0].querySelectorAll('input[data-dtype="Number"], .item-uses input').forEach(t=>{t.addEventListener("wheel",t=>{t.preventDefault(),t.stopPropagation(),t.deltaY<0&&(t.currentTarget.value=Number(t.currentTarget.value)+1),t.deltaY>0&&(t.currentTarget.value=Math.max(Number(t.currentTarget.value)-1,0)),$(t.currentTarget).change()})})}))}s.r(e),s.d(e,"default",(function(){return a}))},"./src/scripts/change-placeables.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(t,e,s){"use strict";function a(t){const{clones:e,destination:s,origin:a,originalEvent:n}=t.data;canvas._onDragCanvasPan(n);const r=s.x-a.x,i=s.y-a.y;let o=!1;if(t.data.previous){const e=30,a=.8,n=t.data.previous,r=t.data.momentum||0,i=t.data.v||{x:0,y:0},l={x:s.x-n.x,y:s.y-n.y},c={x:l.x-i.x,y:l.y-i.y};t.data.momentum=c.x*c.x+c.y*c.y+r*a,o=!t.shiftKey&&t.data.momentum{document.getElementById("chat").classList.contains("active")||(console.log("rendering chat message"),console.log(t,e,s),console.log(document.getElementById("chat").classList.contains("active")))}),document.getElementById("hud").appendChild(document.createElement("div")).classList.add("mess-chat-popup")}s.r(e),s.d(e,"initChatPopUp",(function(){return a}))},"./src/scripts/draggable-lists/draggable-list.js": +/*! exports provided: initChatPopUp */function(e,t,a){"use strict";function s(){Hooks.on("renderChatMessage",(e,t,a)=>{document.getElementById("chat").classList.contains("active")||(console.log("rendering chat message"),console.log(e,t,a),console.log(document.getElementById("chat").classList.contains("active")))}),document.getElementById("hud").appendChild(document.createElement("div")).classList.add("mess-chat-popup")}a.r(t),a.d(t,"initChatPopUp",(function(){return s}))},"./src/scripts/draggable-lists/draggable-list.js": /*!*******************************************************!*\ !*** ./src/scripts/draggable-lists/draggable-list.js ***! \*******************************************************/ -/*! exports provided: DraggableList */function(t,e,s){"use strict";s.r(e),s.d(e,"DraggableList",(function(){return a}));class a{constructor(t,e,s={}){this.container=t,this.selector=e,this.options=mergeObject(this.defaultOptions,s),this._init()}get defaultOptions(){return{offset:21,time:.1,onDragStart:null,onDragEnd:null,onDrop:null}}async _init(){this._items=Array.from(this.container.childNodes).filter(t=>t.matches&&t.matches(this.selector)),this.container.addEventListener("dragleave",t=>{const e=this.container.getBoundingClientRect();!t.insideChild&&t.clientYe.y+5&&t.clientX>e.x+5&&t.clientX{const e=t.insideChild;this._over&&!e&&(Object.defineProperty(t,"target",{writable:!1,value:this._over}),t.insideChild=!0)}),this._items.forEach((t,e)=>this._initItem(t,e))}async _initItem(t,e){t.style.position="relative",t.addEventListener("dragenter",t=>this._onDragEnterItem(t,e)),t.addEventListener("dragleave",t=>this._onDragLeaveItem(t,e)),t.addEventListener("dragend",t=>{const e=t.currentTarget;e.style.opacity=null,e.style.height=null,TweenLite.from(e,this.options.time,{opacity:0,height:0}),this._resetOffsets()}),t.addEventListener("dragstart",t=>{this._dragged=t.currentTarget,TweenLite.to(t.currentTarget,this.options.time,{opacity:0,height:0})})}_onDragEnterItem(t,e){t.stopPropagation(),this._over=this._items[e];const s=this.options.time,a=this.options.offset,n=this._items.slice(0,e);TweenLite.to(n,s,{top:0});const r=this._items.slice(e);return TweenLite.to(r,s,{top:a}),!1}_onDragLeaveItem(t,e){}_resetOffsets(t=this.options.time){TweenLite.to(this._items,t,{top:0})}}},"./src/scripts/draggable-lists/index.js": +/*! exports provided: DraggableList */function(e,t,a){"use strict";a.r(t),a.d(t,"DraggableList",(function(){return s}));class s{constructor(e,t,a={}){this.container=e,this.selector=t,this.options=mergeObject(this.defaultOptions,a),this._init()}get defaultOptions(){return{offset:21,time:.2,dir:null,onDragStart:null,onDragEnd:null,onDrop:null}}async _init(){this._items=Array.from(this.container.querySelectorAll(this.selector)),this.container.addEventListener("dragleave",e=>{const t=this.container.getBoundingClientRect();!e.insideChild&&e.clientYt.y+5&&e.clientX>t.x+5&&e.clientX{}),this._items.forEach((e,t)=>this._initItem(e,t))}async _initItem(e,t){e.style.position="relative",e.addEventListener("dragenter",e=>this._onDragEnterItem(e,t)),e.addEventListener("dragleave",e=>this._onDragLeaveItem(e,t)),e.addEventListener("dragend",e=>{this._resetOffsets()})}_onDragEnterItem(e,t){console.time("dragEnter"),e.stopPropagation();const a=this.options.time,s=this.options.offset;this._resetOffsets();let n=e.currentTarget;if(console.log(e.currentTarget),n.matches(this.options.dir)){const t=Array.from(n.querySelectorAll(this.selector)),s=n.getBoundingClientRect();if(e.clientY>s.top&&e.clientYe.clientY){n=a;break}}}return this._over=n,console.log(n,s),TweenLite.to(n,a,{paddingTop:s}),console.timeEnd("dragEnter"),!1}_onDragLeaveItem(e,t){return e.stopPropagation(),!1}_resetOffsets(e=this.options.time){TweenLite.to(this._items,e,{paddingTop:0})}}},"./src/scripts/draggable-lists/index.js": /*!**********************************************!*\ !*** ./src/scripts/draggable-lists/index.js ***! \**********************************************/ -/*! exports provided: initDraggableLists */function(t,e,s){"use strict";s.r(e),s.d(e,"initDraggableLists",(function(){return n}));var a=s(/*! ./draggable-list */"./src/scripts/draggable-lists/draggable-list.js");function n(){const t=SidebarDirectory.prototype.activateListeners;SidebarDirectory.prototype.activateListeners=function(e){e[0].querySelectorAll(".directory-list, .subdirectory").forEach(t=>new a.DraggableList(t,".entity")),t.call(this,e)},Hooks.on("renderActorSheet",(t,e,s)=>{e[0].querySelectorAll(".item-list").forEach(t=>new a.DraggableList(t,".item"))})}},"./src/scripts/index.js": +/*! exports provided: initDraggableLists */function(e,t,a){"use strict";a.r(t),a.d(t,"initDraggableLists",(function(){return n}));var s=a(/*! ./draggable-list */"./src/scripts/draggable-lists/draggable-list.js");function n(){const e=SidebarDirectory.prototype.activateListeners;SidebarDirectory.prototype.activateListeners=function(t){const a=t[0].querySelector(".directory-list");new s.DraggableList(a,".entity, .folder",{dir:".folder"}),e.call(this,t)},Hooks.on("renderActorSheet",(e,t,a)=>{t[0].querySelectorAll(".item-list").forEach(e=>new s.DraggableList(e,".item"))}),SceneDirectory.prototype._onLazyLoadImage=function(e,t){for(let a of e){if(!a.isIntersecting)continue;const e=a.target;e.dataset.backgroundImage&&(e.children[0].style["background-image"]=`url("${e.dataset.backgroundImage}")`,delete e.dataset.backgroundImage);const s=e.querySelector("img");s&&s.dataset.src&&(s.src=s.dataset.src,delete s.dataset.src),t.unobserve(a.target)}}}},"./src/scripts/index.js": /*!******************************!*\ !*** ./src/scripts/index.js ***! \******************************/ -/*! no exports provided */function(t,e,s){"use strict";s.r(e);var a=s(/*! ./rolls */"./src/scripts/rolls/index.js"),n=s(/*! ./settings.js */"./src/scripts/settings.js"),r=s(/*! ./modify-templates.js */"./src/scripts/modify-templates.js"),i=s(/*! ./change-placeables.js */"./src/scripts/change-placeables.js"),o=s(/*! ./actor-item-sort-btn.js */"./src/scripts/actor-item-sort-btn.js"),l=s(/*! ./prepared-spell-tracker.js */"./src/scripts/prepared-spell-tracker.js"),c=s(/*! ./add-scrolling.js */"./src/scripts/add-scrolling.js"),d=s(/*! ./draggable-lists */"./src/scripts/draggable-lists/index.js");s(/*! ./chat-popup */"./src/scripts/chat-popup/index.js");Hooks.on("ready",(async function(){if(game.settings.get("mess","actor-item-sort")&&Object(o.default)(),game.settings.get("mess","prepared-spell-tracker")&&Object(l.default)(),game.settings.get("mess","add-scrolling")&&Object(c.default)(),CONFIG.debug.mess){(await fromUuid("Actor.xV3LUAg05Pz5MFTS")).sheet.render(!0)}if(!game.user.isGM)return;const t=game.modules.get("mess"),e=t.data.title,a=t.data.version;game.settings.register(e,"version",{name:e+" Version",default:"0.0.0",type:String,scope:"world"});const n=game.settings.get(e,"version");isNewerVersion(a,n)&&(await s.e(/*! import() | welcome-screen */"welcome-screen").then(s.bind(null,/*! ./welcome-screen.js */"./src/scripts/welcome-screen.js"))).default()})),Hooks.on("init",(function(){game.mess={},CONFIG.debug.mess=!1,n.MessSettings.init(),Object(a.rolling)(),Object(r.dndTemplateSettings)(),Object(r.changeTemplates)(),game.settings.get("mess","change-placeables")&&Object(i.changePlaceables)(),game.settings.get("mess","better-draggable-lists")&&Object(d.initDraggableLists)()}))},"./src/scripts/modify-templates.js": +/*! no exports provided */function(e,t,a){"use strict";a.r(t);var s=a(/*! ./rolls */"./src/scripts/rolls/index.js"),n=a(/*! ./settings.js */"./src/scripts/settings.js"),r=a(/*! ./modify-templates.js */"./src/scripts/modify-templates.js"),i=a(/*! ./change-placeables.js */"./src/scripts/change-placeables.js"),o=a(/*! ./actor-item-sort-btn.js */"./src/scripts/actor-item-sort-btn.js"),l=a(/*! ./prepared-spell-tracker.js */"./src/scripts/prepared-spell-tracker.js"),c=a(/*! ./add-scrolling.js */"./src/scripts/add-scrolling.js"),d=a(/*! ./draggable-lists */"./src/scripts/draggable-lists/index.js");a(/*! ./chat-popup */"./src/scripts/chat-popup/index.js");Hooks.on("ready",(async function(){if(game.settings.get("mess","actor-item-sort")&&Object(o.default)(),game.settings.get("mess","prepared-spell-tracker")&&Object(l.default)(),game.settings.get("mess","add-scrolling")&&Object(c.default)(),CONFIG.debug.mess){(await fromUuid("Actor.xV3LUAg05Pz5MFTS")).sheet.render(!0)}if(!game.user.isGM)return;const e=game.modules.get("mess"),t=e.data.title,s=e.data.version;game.settings.register(t,"version",{name:t+" Version",default:"0.0.0",type:String,scope:"world"});const n=game.settings.get(t,"version");isNewerVersion(s,n)&&(await a.e(/*! import() | welcome-screen */"welcome-screen").then(a.bind(null,/*! ./welcome-screen.js */"./src/scripts/welcome-screen.js"))).default()})),Hooks.on("init",(function(){game.mess={},CONFIG.debug.mess=!1,n.MessSettings.init(),Object(s.rolling)(),Object(r.dndTemplateSettings)(),Object(r.changeTemplates)(),game.settings.get("mess","change-placeables")&&Object(i.changePlaceables)(),game.settings.get("mess","better-draggable-lists")&&Object(d.initDraggableLists)()}))},"./src/scripts/modify-templates.js": /*!*****************************************!*\ !*** ./src/scripts/modify-templates.js ***! \*****************************************/ -/*! exports provided: changeTemplates, dndTemplateSettings */function(t,e,s){"use strict";function a(){let t=MeasuredTemplate.prototype.refresh.toString();if(game.settings.get("mess","modify-templates")&&(t=t.replace(/this\.template\.beginTextureFill\(\{[\s\S]*\}\)\;/,'\n\t\t\t{\n\t\t\t\tlet mat = PIXI.Matrix.IDENTITY;\n\t\t\t\t// rectangle\n\t\t\t\tif (this.shape.width && this.shape.height)\n\t\t\t\t\tmat.scale(this.shape.width / this.texture.width, this.shape.height / this.texture.height);\n\t\t\t\telse if (this.shape.radius) {\n\t\t\t\t\tmat.scale(this.shape.radius * 2 / this.texture.height, this.shape.radius * 2 / this.texture.width)\n\t\t\t\t\t// Circle center is texture start...\n\t\t\t\t\tmat.translate(-this.shape.radius, -this.shape.radius);\n\t\t\t\t} else if (this.data.t === "ray") {\n\t\t\t\t\tconst d = canvas.dimensions,\n\t\t\t\t\t\t\t\theight = this.data.width * d.size / d.distance,\n\t\t\t\t\t\t\t\twidth = this.data.distance * d.size / d.distance;\n\t\t\t\t\tmat.scale(width / this.texture.width, height / this.texture.height);\n\t\t\t\t\tmat.translate(0, -height * 0.5);\n\n\t\t\t\t\tmat.rotate(toRadians(this.data.direction));\n\t\t\t\t} else {// cone\n\t\t\t\t\tconst d = canvas.dimensions;\n\t\t\t\n\t\t\t\t\t// Extract and prepare data\n\t\t\t\t\tlet {direction, distance, angle} = this.data;\n\t\t\t\t\tdistance *= (d.size / d.distance);\n\t\t\t\t\tdirection = toRadians(direction);\n\t\t\t\t\tconst width = this.data.distance * d.size / d.distance;\n\n\t\t\t\t\tconst angles = [(angle/-2), (angle/2)];\n\t\t\t\t\tdistance = distance / Math.cos(toRadians(angle/2));\n\t\t\t\n\t\t\t\t\t// Get the cone shape as a polygon\n\t\t\t\t\tconst rays = angles.map(a => Ray.fromAngle(0, 0, direction + toRadians(a), distance+1));\n\t\t\t\t\tconst height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x)\n\t\t\t\t\t\t\t\t\t\t\t\t\t+ (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y));\n\t\t\t\t\tmat.scale(width / this.texture.width, height / this.texture.height);\n\t\t\t\t\tmat.translate(0, -height/2)\n\t\t\t\t\tmat.rotate(toRadians(this.data.direction));\n\t\t\t\t}\n\t\t\t\tthis.template.beginTextureFill({\n\t\t\t\t\ttexture: this.texture,\n\t\t\t\t\tmatrix: mat,\n\t\t\t\t\talpha: 0.8\n\t\t\t\t});\n\t\t\t\t// move into draw or so\n\t\t\t\tconst source = getProperty(this.texture, "baseTexture.resource.source")\n\t\t\t\tif ( source && (source.tagName === "VIDEO") ) {\n\t\t\t\t\tsource.loop = true;\n\t\t\t\t\tsource.muted = true;\n\t\t\t\t\tgame.video.play(source);\n\t\t\t\t}\n\t\t}'),Hooks.on("renderMeasuredTemplateConfig",(t,e,s)=>{e[0].querySelector(".file-picker").dataset.type="imagevideo"})),game.settings.get("mess","templateAutoTargeting")&&(t=t.replace(/this\.\_borderThickness/,"this.texture && !this._hover ? 0 : this._borderThickness"),t=t.replace(/return\sthis\;/,"\n\t\t\tconst grid = canvas.grid;\n\t\t\t// only show grid highlights on hover\n\t\t\tif (this.texture) {\n\t\t\t\tconst hl = grid.getHighlightLayer(`Template.${this.id}`);\n\t\t\t\tif (hl)\n\t\t\t\t\thl.renderable = this._hover;\n\t\t\t}\n\t\t\treturn this;")),MeasuredTemplate.prototype.refresh=Function(`"use strict"; return ( function ${t} )`)(),game.settings.get("mess","templateAutoTargeting")){MeasuredTemplate.prototype.getTargets=i,MeasuredTemplate.prototype.isTokenInside=r;const t=MeasuredTemplate.prototype._onDragLeftMove;MeasuredTemplate.prototype._onDragLeftMove=function(e){const s=t.bind(this)(e);for(let t of e.data.clones)this.getTargets(t);return s}}}async function n(){if("dnd5e"!==game.system.id)return;const t=await import("/systems/dnd5e/module/pixi/ability-template.js"),e=t.default||t.AbilityTemplate;if(game.settings.get("mess","modify-templates")){Hooks.on("renderItemSheet",o);const t=e.fromItem;e.fromItem=function(e){const s=t.bind(this)(e);let a=e.getFlag("mess","templateTexture");if(!a&&e.hasDamage){const t=game.settings.get("mess","templateTexture")||{};a=t[e.data.data.damage.parts[0][1]]||{},a=a[s.data.t]}return a&&loadTexture(a).then(t=>{s.texture=t,s.data.texture=a,s.refresh()}),s.item=e,s}}if(game.settings.get("mess","templateAutoTargeting")){const t=e.prototype.activatePreviewListeners.toString().replace(/this\.refresh\(\)\;/,"this.refresh();\n\t\t\t\t\t\tthis.getTargets(this);\n\t\t\t\t\t");e.prototype.getTargets=i,e.prototype.isTokenInside=r,e.prototype.activatePreviewListeners=Function(`"use strict"; return ( function ${t} )`)()}}function r(t){const e=canvas.scene.data.grid,s=this.data.x,a=this.data.y,n=t.width>=1?.5:t.width/2,r=t.height>=1?.5:t.height/2;for(let i=n;i\n\t\t\t\n\t\t\n\t');const r=a.querySelector("button");r.style.flex="0",t._activateFilePicker(r);const i=e[0].querySelector('[name="data.target.units"]');i&&i.closest(".form-group").after(s)}s.r(e),s.d(e,"changeTemplates",(function(){return a})),s.d(e,"dndTemplateSettings",(function(){return n}))},"./src/scripts/prepared-spell-tracker.js": +/*! exports provided: changeTemplates, dndTemplateSettings */function(e,t,a){"use strict";function s(){let e=MeasuredTemplate.prototype.refresh.toString();if(game.settings.get("mess","modify-templates")&&(e=e.replace(/this\.template\.beginTextureFill\(\{[\s\S]*\}\)\;/,'\n\t\t\t{\n\t\t\t\tlet mat = PIXI.Matrix.IDENTITY;\n\t\t\t\t// rectangle\n\t\t\t\tif (this.shape.width && this.shape.height)\n\t\t\t\t\tmat.scale(this.shape.width / this.texture.width, this.shape.height / this.texture.height);\n\t\t\t\telse if (this.shape.radius) {\n\t\t\t\t\tmat.scale(this.shape.radius * 2 / this.texture.height, this.shape.radius * 2 / this.texture.width)\n\t\t\t\t\t// Circle center is texture start...\n\t\t\t\t\tmat.translate(-this.shape.radius, -this.shape.radius);\n\t\t\t\t} else if (this.data.t === "ray") {\n\t\t\t\t\tconst d = canvas.dimensions,\n\t\t\t\t\t\t\t\theight = this.data.width * d.size / d.distance,\n\t\t\t\t\t\t\t\twidth = this.data.distance * d.size / d.distance;\n\t\t\t\t\tmat.scale(width / this.texture.width, height / this.texture.height);\n\t\t\t\t\tmat.translate(0, -height * 0.5);\n\n\t\t\t\t\tmat.rotate(toRadians(this.data.direction));\n\t\t\t\t} else {// cone\n\t\t\t\t\tconst d = canvas.dimensions;\n\t\t\t\n\t\t\t\t\t// Extract and prepare data\n\t\t\t\t\tlet {direction, distance, angle} = this.data;\n\t\t\t\t\tdistance *= (d.size / d.distance);\n\t\t\t\t\tdirection = toRadians(direction);\n\t\t\t\t\tconst width = this.data.distance * d.size / d.distance;\n\n\t\t\t\t\tconst angles = [(angle/-2), (angle/2)];\n\t\t\t\t\tdistance = distance / Math.cos(toRadians(angle/2));\n\t\t\t\n\t\t\t\t\t// Get the cone shape as a polygon\n\t\t\t\t\tconst rays = angles.map(a => Ray.fromAngle(0, 0, direction + toRadians(a), distance+1));\n\t\t\t\t\tconst height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x)\n\t\t\t\t\t\t\t\t\t\t\t\t\t+ (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y));\n\t\t\t\t\tmat.scale(width / this.texture.width, height / this.texture.height);\n\t\t\t\t\tmat.translate(0, -height/2)\n\t\t\t\t\tmat.rotate(toRadians(this.data.direction));\n\t\t\t\t}\n\t\t\t\tthis.template.beginTextureFill({\n\t\t\t\t\ttexture: this.texture,\n\t\t\t\t\tmatrix: mat,\n\t\t\t\t\talpha: 0.8\n\t\t\t\t});\n\t\t\t\t// move into draw or so\n\t\t\t\tconst source = getProperty(this.texture, "baseTexture.resource.source")\n\t\t\t\tif ( source && (source.tagName === "VIDEO") ) {\n\t\t\t\t\tsource.loop = true;\n\t\t\t\t\tsource.muted = true;\n\t\t\t\t\tgame.video.play(source);\n\t\t\t\t}\n\t\t}'),Hooks.on("renderMeasuredTemplateConfig",(e,t,a)=>{t[0].querySelector(".file-picker").dataset.type="imagevideo"})),game.settings.get("mess","templateAutoTargeting")&&(e=e.replace(/this\.\_borderThickness/,"this.texture && !this._hover ? 0 : this._borderThickness"),e=e.replace(/return\sthis\;/,"\n\t\t\tconst grid = canvas.grid;\n\t\t\t// only show grid highlights on hover\n\t\t\tif (this.texture) {\n\t\t\t\tconst hl = grid.getHighlightLayer(`Template.${this.id}`);\n\t\t\t\tif (hl)\n\t\t\t\t\thl.renderable = this._hover;\n\t\t\t}\n\t\t\treturn this;")),MeasuredTemplate.prototype.refresh=Function(`"use strict"; return ( function ${e} )`)(),game.settings.get("mess","templateAutoTargeting")){MeasuredTemplate.prototype.getTargets=i,MeasuredTemplate.prototype.isTokenInside=r;const e=MeasuredTemplate.prototype._onDragLeftMove;MeasuredTemplate.prototype._onDragLeftMove=function(t){const a=e.bind(this)(t);for(let e of t.data.clones)this.getTargets(e);return a}}}async function n(){if("dnd5e"!==game.system.id)return;const e=await import("/systems/dnd5e/module/pixi/ability-template.js"),t=e.default||e.AbilityTemplate;if(game.settings.get("mess","modify-templates")){Hooks.on("renderItemSheet",o);const e=t.fromItem;t.fromItem=function(t){const a=e.bind(this)(t);let s=t.getFlag("mess","templateTexture");if(!s&&t.hasDamage){const e=game.settings.get("mess","templateTexture")||{};s=e[t.data.data.damage.parts[0][1]]||{},s=s[a.data.t]}return s&&loadTexture(s).then(e=>{a.texture=e,a.data.texture=s,a.refresh()}),a.item=t,a}}if(game.settings.get("mess","templateAutoTargeting")){const e=t.prototype.activatePreviewListeners.toString().replace(/this\.refresh\(\)\;/,"this.refresh();\n\t\t\t\t\t\tthis.getTargets(this);\n\t\t\t\t\t");t.prototype.getTargets=i,t.prototype.isTokenInside=r,t.prototype.activatePreviewListeners=Function(`"use strict"; return ( function ${e} )`)()}}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 i=n;i\n\t\t\t\n\t\t\n\t');const r=s.querySelector("button");r.style.flex="0",e._activateFilePicker(r);const i=t[0].querySelector('[name="data.target.units"]');i&&i.closest(".form-group").after(a)}a.r(t),a.d(t,"changeTemplates",(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(t,e,s){"use strict";async function a(){Hooks.on("renderActorSheet",(async function(t,e,s){const a=await fromUuid("Actor."+s.actor._id);if(a.isNPC)return;if(!e[0].querySelector(".spell-dc"))return;let n=document.createElement("div");n.classList.add("spell-detail"),n.classList.add("spell-slots"),n.style.flex="1",n.style.border="none",n.appendChild(document.createElement("span")).innerText=`${game.i18n.localize("MESS.actorSheet.preparedSpellTracker")}: ${s.preparedSpells}`;const r=n.appendChild(document.createElement("span"));r.innerText=" / ",r.classList.add("sep");const i=n.appendChild(document.createElement("input"));if(i.type="text",i.value=a.getFlag("mess","maxPreparedSpells")||0,i.addEventListener("change",(async function(t){t.preventDefault(),t.stopPropagation();const e=Number(t.currentTarget.value);return isNaN(e)?(t.currentTarget.value=a.getFlag("mess","maxPreparedSpells")||0,!1):(a.setFlag("mess","maxPreparedSpells",e),!1)})),"Tidy5eSheet"===t.constructor.name){const t=e[0].querySelector(".spellcasting-ability");t.appendChild(n,t)}else{const t=e[0].querySelector(".spellbook .inventory-list");n.style.flex="0",n.style.alignSelf="flex-start",n.style.margin="0 8px",t.parentNode.insertBefore(n,t)}}))}s.r(e),s.d(e,"default",(function(){return a}))},"./src/scripts/rolls/apply-dmg.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);if(s.isNPC)return;if(!t[0].querySelector(".spell-dc"))return;let n=document.createElement("div");n.classList.add("spell-detail"),n.classList.add("spell-slots"),n.style.flex="1",n.style.border="none",n.appendChild(document.createElement("span")).innerText=`${game.i18n.localize("MESS.actorSheet.preparedSpellTracker")}: ${a.preparedSpells}`;const r=n.appendChild(document.createElement("span"));r.innerText=" / ",r.classList.add("sep");const i=n.appendChild(document.createElement("input"));if(i.type="text",i.value=s.getFlag("mess","maxPreparedSpells")||0,i.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)})),"Tidy5eSheet"===e.constructor.name){const e=t[0].querySelector(".spellcasting-ability");if(!e)return;e.appendChild(n,e)}else{const e=t[0].querySelector(".spellbook .inventory-list");if(!e)return;n.style.flex="0",n.style.alignSelf="flex-start",n.style.margin="0 8px",e.parentNode.insertBefore(n,e)}}))}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(t,e,s){"use strict";function a(){Hooks.on("getChatLogEntryContext",(function(t,e){setProperty(game,"mess.chatLogEntryContextOptions",e);const s=t=>canvas.tokens.controlled.length&&t.find(".dice-roll .dice-result").length;for(let t=1;tNumber(e.querySelector("span").innerText)).reduce((e,t)=>e+t,0)}function n(e){const t=Array.from(e[0].parentNode.querySelectorAll(".mess-dice-result"));return t.length>1&&t[1].classList.contains("mess-versatile")&&t.splice(0,1),t.map(e=>Number(e.querySelector("span").innerText)).reduce((e,t)=>e+t,0)}function r(){const e=e=>!0,t=e=>e[0].parentNode.querySelector(".mess-dice-result:not(.mess-versatile)"),a=e=>e[0].parentNode.querySelector(".mess-versatile"),r=e=>!!e[0].closest(".mess-attack-card").dataset.targetId,l=e=>!r(e),c=[{name:game.i18n.localize("MESS.attackCard.contextMenu.applyTarget"),condition:r},{name:game.i18n.localize("MESS.attackCard.contextMenu.applySelected"),condition:l},{name:game.i18n.localize("MESS.attackCard.contextMenu.dmg"),condition:t,icon:''},{name:"full",icon:"",condition:t,callback:(e,t)=>o(t,1),content:e=>`${s(e)}`},{name:"half",icon:"",condition:t,callback:(e,t)=>o(t,1),content:e=>`${Math.max(Math.floor(.5*s(e)),1)}`},{name:"double",icon:"",condition:t,callback:(e,t)=>o(t,1),content:e=>`${Math.floor(2*s(e))}`},{name:game.i18n.localize("MESS.attackCard.contextMenu.healing"),condition:t,icon:''},{name:"full",icon:"",condition:t,callback:(e,t)=>o(t,-1),content:e=>`${s(e)}`},{name:[game.i18n.localize("DND5E.Versatile")," - ",game.i18n.localize("MESS.attackCard.contextMenu.dmg")],condition:a,icon:''},{name:"full",icon:"",condition:a,callback:(e,t)=>o(t,1),content:e=>`${n(e)}`},{name:"half",icon:"",condition:a,callback:(e,t)=>o(t,1),content:e=>`${Math.max(Math.floor(.5*n(e)),1)}`},{name:"double",icon:"",condition:a,callback:(e,t)=>o(t,1),content:e=>`${Math.floor(2*n(e))}`},{name:[game.i18n.localize("DND5E.Versatile")," - ",game.i18n.localize("MESS.attackCard.contextMenu.healing")],condition:a,icon:''},{name:"full",icon:"",condition:a,callback:(e,t)=>o(t,-1),content:e=>`${n(e)}`}],d=[{name:game.i18n.localize("MESS.attackCard.contextMenu.applyTarget"),condition:r},{name:game.i18n.localize("MESS.attackCard.contextMenu.applySelected"),condition:l},{name:game.i18n.localize("MESS.attackCard.contextMenu.dmg"),condition:e,icon:''},{name:"full",icon:"",condition:e,callback:(e,t)=>o(t,1),content:e=>`${e[0].querySelector("span").innerText}`},{name:"half",icon:"",condition:e,callback:(e,t)=>o(t,1),content:e=>`${Math.max(Math.floor(.5*Number(e[0].querySelector("span").innerText)),1)}`},{name:"double",icon:"",condition:e,callback:(e,t)=>o(t,1),content:e=>`${Math.floor(2*Number(e[0].querySelector("span").innerText))}`},{name:game.i18n.localize("MESS.attackCard.contextMenu.healing"),condition:e,icon:''},{name:"full",icon:"",condition:e,callback:(e,t)=>o(t,-1),content:e=>`${e[0].querySelector("span").innerText}`}];ContextMenu.prototype.bind=Function('"use strict"; return ( function '+ContextMenu.prototype.bind.toString().replace(/this\.render\(parent\)/,"this.render(parent, event)")+")")(),Hooks.on("renderChatLog",(e,t,a)=>{new i(t.find("#chat-log"),".mess-chat-dmg .mess-dice-result",d),new i(t.find("#chat-log"),".mess-chat-dmg .mess-chat-roll-type",c)}),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!t.condition||(t.condition instanceof Function?t.condition(e):t.condition));if(!a.length)return;t.stopPropagation(),this.menuItems=a.map(t=>(t.content&&(t.name=t.content(e)),t));let s=$("#context-menu").length?$("#context-menu"):$(''),n=$('
    ');s.html(n);const r=document.createDocumentFragment();for(let t of this.menuItems){const a=document.createElement("li");t.name instanceof Array&&(t.name=t.name.map(e=>game.i18n.localize(e)).join("")),t.icon?a.innerHTML=`${t.icon}${game.i18n.localize(t.name)}`:a.innerHTML=game.i18n.localize(t.name),t.callback?(a.addEventListener("click",s=>{s.preventDefault(),s.stopPropagation(),t.callback(e,a),this.close()}),a.classList.add("context-item")):a.classList.add("mess-context-menu-header"),r.appendChild(a)}return n[0].appendChild(r),this._setPosition(s,e),this._animateOpen(s)}_setPosition(e,t){e.removeClass("expand-up expand-down"),super._setPosition(e,t)}}async function o(e,t){const a=Number(e.querySelector("span").innerText),s=e.closest(".mess-attack-card"),n=s.dataset.targetId;if(n){const e=s.dataset.sceneId,r=game.scenes.get(e);if(!r)return void ui.notifications.error(game.i18n.localize("MESS.attackCard.applyDamage.sceneNotFound"));const i=new Token(r.getEmbeddedEntity("Token",n));if(!i)return void ui.notifications.error(game.i18n.localize("MESS.attackCard.applyDamage.targetNotFound"));if(!i.owner)return void ui.notifications.error(game.i18n.localize("MESS.attackCard.applyDamage.targetNotOwner"));i.actor.applyDamage(a,t)}else{const e=canvas.tokens.controlled;if(!e.length)return void ui.notifications.error(game.i18n.localize("MESS.attackCard.contextMenu.error"));for(const s of e){s.actor.applyDamage(a,t)}}}},"./src/scripts/rolls/controls.js": /*!***************************************!*\ !*** ./src/scripts/rolls/controls.js ***! \***************************************/ -/*! exports provided: default */function(t,e,s){"use strict";async function a(t,e,s){e[0].classList.add("mess");const a=document.createElement("div");a.classList.add("mess-roll-control");const n=game.settings.get("mess",game.userId+".adv-selector"),r={advantage:"advantage"===n,normal:"normal"===n,disadvantage:"disadvantage"===n,...game.settings.get("mess",game.userId+".autoroll-selector")};a.insertAdjacentHTML("afterbegin",await renderTemplate("modules/mess/templates/roll-control.html",r)),a.querySelectorAll(".mess-adv-selector a").forEach(t=>{t.addEventListener("click",(async function(t){t.preventDefault(),t.stopPropagation();const e=Array.from(t.currentTarget.parentNode.querySelectorAll("a")),s=e.findIndex(t=>t.classList.contains("mess-selected"));e[s].classList.remove("mess-selected");const a=e[(s+1)%e.length];a.classList.add("mess-selected"),game.settings.set("mess",game.userId+".adv-selector",a.name)})),t.addEventListener("contextmenu",(async function(t){t.preventDefault(),t.stopPropagation();const e=Array.from(t.currentTarget.parentNode.querySelectorAll("a")),s=e.findIndex(t=>t.classList.contains("mess-selected"));e[s].classList.remove("mess-selected");const a=e[(s+e.length-1)%e.length];a.classList.add("mess-selected"),game.settings.set("mess",game.userId+".adv-selector",a.name)}))}),a.querySelectorAll(".mess-autoroll-selector a").forEach(t=>{t.addEventListener("click",(async function(t){t.preventDefault(),t.stopPropagation(),t.currentTarget.classList.toggle("mess-selected");let e=game.settings.get("mess",game.userId+".autoroll-selector");e[t.currentTarget.name]=t.currentTarget.classList.contains("mess-selected"),game.settings.set("mess",game.userId+".autoroll-selector",e)}))});const i=document.getElementById("chat-controls");i.insertBefore(a,i.childNodes[0])}s.r(e),e.default=function(){Hooks.on("renderChatLog",a),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": +/*! exports provided: default */function(e,t,a){"use strict";async function s(e,t,a){t[0].classList.add("mess"),console.log("qwe",game.user,game.user.isGM),game.user.isGM&&t[0].classList.add("mess-is-gm");const s=document.createElement("div");s.classList.add("mess-roll-control");const n=game.settings.get("mess",game.userId+".adv-selector"),r={advantage:"advantage"===n,normal:"normal"===n,disadvantage:"disadvantage"===n,...game.settings.get("mess",game.userId+".autoroll-selector")};s.insertAdjacentHTML("afterbegin",await renderTemplate("modules/mess/templates/roll-control.html",r)),s.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.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 i=document.getElementById("chat-controls");i.insertBefore(s,i.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(t,e,s){"use strict";function a(){return document.getElementById("mess-roll-mod").value}function n(){return game.settings.get("mess",game.userId+".adv-selector")}async function r(t){let e=n(),s=1,r=t.halflingLucky?"r=1":"";"advantage"===e?(s=t.elvenAccuracy?3:2,r+="kh",t.title+=` (${game.i18n.localize("DND5E.Advantage")})`):"disadvantage"===e&&(s=2,r+="kl",t.title+=` (${game.i18n.localize("DND5E.Disadvantage")})`);let i=`${s}d20${r}`;t.reliableTalent&&(i=`{${s}d20${r},10}kh`),t.parts.unshift(i);const o=a();o&&t.parts.push(o);let l=new Roll(t.parts.join("+"),t);l.roll();const c=l.parts[0].total;let d={...t,tooltip:await l.getTooltip(),roll:l,crit:c>=20,fumble:c<=1};const m=await renderTemplate("modules/mess/templates/roll-card.html",d);let g={user:game.user._id,type:CONST.CHAT_MESSAGE_TYPES.OTHER,content:m,speaker:{actor:this,alias:this.name}},u=game.settings.get("core","rollMode");["gmroll","blindroll"].includes(u)&&(g.whisper=ChatMessage.getWhisperIDs("GM")),"blindroll"===u&&(g.blind=!0),ChatMessage.create(g)}async function i({actor:t,item:e}){if(!e.hasAttack)return null;const s=t.data.data,n=e.data.data,r=t.data.flags.dnd5e||{};let i=e.getRollData();const o=["@mod"];("weapon"!==e.data.type||n.proficient)&&o.push("@prof"),i.parts=o,"weapon"===e.data.type&&r.weaponCriticalThreshold&&(i.critical=parseInt(r.weaponCriticalThreshold)),["weapon","spell"].includes(e.data.type)&&r.elvenAccuracy&&["dex","int","wis","cha"].includes(e.abilityMod)&&(i.elvenAccuracy=!0),r.halflingLucky&&(i.halflingLucky=!0);const l=s.bonuses[n.actionType]||{};(n.attackBonus||l.attack)&&(i.atk=[n.attackBonus,l.attack].filterJoin(" + "),isNaN(Number(i.atk))||o.push("@atk"));let c=new Roll(i.parts.join("+"),i);i.totalModifier=c._safeEval(c.formula),i.totalModifier=i.totalModifier>=0?"+"+i.totalModifier:i.totalModifier,i.atk&&!c._formula.includes("@atk")&&(i.parts.push("@atk"),c=new Roll(i.parts.join("+"),i),i.totalModifier+="+"+i.atk);const d=a();return d&&(i.parts.push(d),c=new Roll(i.parts.join("+"),i),i.totalModifier+="+"+d),i.formula=c.formula,i.terms=c._formula,i}async function o(t){const e=t.currentTarget;e.disabled=!0;const s=e.closest(".chat-card"),a=s.closest(".message").dataset.messageId;if(a){const t=game.messages.get(a);if(!t.owner&&!t.isAuthor)return void ui.notifications.error("You do not own the permissions to make that roll!")}const r=CONFIG.Item.entityClass._getChatCardActor(s);if(!r.owner)return!1;const o=r.getOwnedItem(s.dataset.itemId);if(!o)return ui.notifications.error(`The requested item ${s.dataset.itemId} no longer exists on Actor ${r.name}`);let l=await i({actor:r,item:o}),c=n(),d=1,m=l.halflingLucky?"r=1":"";"advantage"===c?(d=l.elvenAccuracy?3:2,m+="kh"):"disadvantage"===c&&(d=2,m+="kl"),l.parts.unshift(`${d}d20${m}`);let g=new Roll(l.parts.join("+"),l);g.roll();let u=document.createElement("div");u.title=`${l.parts[0]}+${l.terms} = ${g.formula} = ${g.total}. Click to see rolls.`,u.classList.add("dice-roll"),u.classList.add("mess-dice-result");const p=u.appendChild(document.createElement("span"));p.innerText=g.total,u.insertAdjacentHTML("beforeend",await g.getTooltip()),u.childNodes[1].classList.add("hidden");const f=l.critical||20,h=l.fumble||1,y=g.parts[0].total;if(y>=f&&(p.classList.add("crit"),s.querySelector(".mess-chat-dmg .mess-chat-roll-type").innerHTML+=" - Crit!",s.querySelectorAll(".mess-button-dmg").forEach((t,e)=>{const s=new RegExp(Die.rgx.die,"g"),a=t.dataset.formula.replace(s,(t,e,s,a)=>(game.settings.get("mess","max-critical")?a=" + "+e*s+(a||""):(e*=2,a=a||""),e+"d"+s+a));t.innerHTML=' '+a,t.dataset.formula=a})),y<=h&&p.classList.add("fumble"),t.currentTarget.parentNode.replaceChild(u,t.currentTarget),a){game.messages.get(a).update({content:s.parentNode.innerHTML})}}async function l({actor:t,item:e,spellLevel:s=null}){if(!e.hasDamage)return null;const a=t.data.data,n=e.data.data;let r=e.getRollData();if(s&&(r.item.level=s),r.parts=duplicate(n.damage.parts),n.damage.versatile&&r.parts.splice(1,0,[n.damage.versatile,"versatile"]),!e.getFlag("mess","isBonusDamage"))for(let s of t.items){if(s.id===e.id)continue;let t=[];if(s.getFlag("mess","isBonusDamage")&&s.hasDamage){const e=s.data.data;t.push(e.damage.parts[0][0]);var i=s.name;e.damage.parts[0][1].length>0&&(i+=" - "+game.i18n.localize("DND5E.Damage"+CONFIG.DND5E.damageTypes[e.damage.parts[0][1]])),t.push(i)}t.length>0&&r.parts.push(t)}if("spell"===e.data.type)if("cantrip"===n.scaling.mode){let s=[r.parts[0][0]];const i="character"===t.data.type?a.details.level:a.details.spellLevel;e._scaleCantripDamage(s,i,n.scaling.formula),r.parts[0][0]=s[0]}else if(s&&"level"===n.scaling.mode&&n.scaling.formula){let t=[];e._scaleSpellDamage(t,n.level,s,n.scaling.formula),t.length>0&&(t.push("upcast dice"),r.parts.push(t))}const o=a.bonuses[n.actionType]||{};o.damage&&0!==parseInt(o.damage)&&(r.parts[0][0]+="+@dmg",r.dmg=o.damage);for(let t of r.parts){let e=new Roll(t[0],r);CONFIG.DND5E.damageTypes[t[1]]?t[1]=game.i18n.localize("DND5E.Damage"+CONFIG.DND5E.damageTypes[t[1]]):"versatile"===t[1]&&(t[1]=game.i18n.localize("DND5E.Versatile"));let s=e._evalParentheticalTerms(e.formula).map(t=>t instanceof Roll?(t.roll(),t.total):t);t.push(s.join(""))}return r}async function c(t){const e=t.currentTarget;e.disabled=!0;const s=e.closest(".chat-card"),a=s.closest(".message").dataset.messageId;if(a){const t=game.messages.get(a);if(!t.owner&&!t.isAuthor)return void ui.notifications.error("You do not own the permissions to make that roll!")}const n=e.dataset.formula;let r=new Roll(n);r.roll();let i=document.createElement("div");if(i.title=`${e.dataset.terms} = ${r.formula} = ${r.total}. Click to see rolls.`,i.classList.add("dice-roll"),i.classList.add("mess-dice-result"),i.appendChild(document.createElement("span")).innerText=r.total,i.insertAdjacentHTML("beforeend",await r.getTooltip()),i.childNodes[1].classList.add("hidden"),t.currentTarget.parentNode.replaceChild(i,t.currentTarget),a){game.messages.get(a).update({content:s.parentNode.innerHTML})}}s.r(e),s.d(e,"rollD20",(function(){return r})),s.d(e,"getToHitData",(function(){return i})),s.d(e,"rollToHit",(function(){return o})),s.d(e,"getDmgData",(function(){return l})),s.d(e,"rollDmg",(function(){return c}))},"./src/scripts/rolls/index.js": +/*! exports provided: rollD20, getToHitData, rollToHit, getDmgData, rollDmg */function(e,t,a){"use strict";a.r(t),a.d(t,"rollD20",(function(){return r})),a.d(t,"getToHitData",(function(){return i})),a.d(t,"rollToHit",(function(){return o})),a.d(t,"getDmgData",(function(){return l})),a.d(t,"rollDmg",(function(){return c}));a(/*! ./util.js */"./src/scripts/rolls/util.js");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 i=`${a}d20${r}`;e.reliableTalent&&(i=`{${a}d20${r},10}kh`),e.parts.unshift(i);const o=s();o&&e.parts.push(o);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 m=await renderTemplate("modules/mess/templates/roll-card.html",d);let u={user:game.user._id,type:CONST.CHAT_MESSAGE_TYPES.OTHER,content:m,speaker:{actor:this,alias:this.name}},g=game.settings.get("core","rollMode");["gmroll","blindroll"].includes(g)&&(u.whisper=ChatMessage.getWhisperIDs("GM")),"blindroll"===g&&(u.blind=!0),ChatMessage.create(u)}async function i({actor:e,item:t}){if(!t.hasAttack)return null;const a=e.data.data,n=t.data.data,r=e.data.flags.dnd5e||{};let i=t.getRollData();const o=["@mod"];("weapon"!==t.data.type||n.proficient)&&o.push("@prof"),i.parts=o,"weapon"===t.data.type&&r.weaponCriticalThreshold&&(i.critical=parseInt(r.weaponCriticalThreshold)),["weapon","spell"].includes(t.data.type)&&r.elvenAccuracy&&["dex","int","wis","cha"].includes(t.abilityMod)&&(i.elvenAccuracy=!0),r.halflingLucky&&(i.halflingLucky=!0);const l=a.bonuses[n.actionType]||{};(n.attackBonus||l.attack)&&(i.atk=[n.attackBonus,l.attack].filterJoin(" + "),isNaN(Number(i.atk))||o.push("@atk"));let c=new Roll(i.parts.join("+"),i);i.totalModifier=c._safeEval(c.formula),i.totalModifier=i.totalModifier>=0?"+"+i.totalModifier:i.totalModifier,i.atk&&!c._formula.includes("@atk")&&(i.parts.push("@atk"),c=new Roll(i.parts.join("+"),i),i.totalModifier+="+"+i.atk);const d=s();return d&&(i.parts.push(d),c=new Roll(i.parts.join("+"),i),i.totalModifier+="+"+d),i.formula=c.formula,i.terms=c._formula,i}async function o(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 ui.notifications.error("You do not own the permissions to make that roll!"),!1}const r=CONFIG.Item.entityClass._getChatCardActor(a);if(!r.owner)return!1;const o=r.getOwnedItem(a.dataset.itemId);if(!o)return ui.notifications.error(`The requested item ${a.dataset.itemId} no longer exists on Actor ${r.name}`);let l=await i({actor:r,item:o}),c=n(),d=1,m=l.halflingLucky?"r=1":"";"advantage"===c?(d=l.elvenAccuracy?3:2,m+="kh"):"disadvantage"===c&&(d=2,m+="kl"),l.parts.unshift(`${d}d20${m}`);let u=new Roll(l.parts.join("+"),l);u.roll();let g=document.createElement("div");g.classList.add("dice-roll"),g.classList.add("mess-dice-result"),game.user.isGM&&g.classList.add("mess-gm-dice");const p=g.appendChild(document.createElement("span"));p.classList.add("mess-roll-container"),p.innerHTML=`${u.total}`,g.insertAdjacentHTML("beforeend",await u.getTooltip());let f="";const h=l.terms.split("+"),y=l.formula.split(/(?=[+-])/);for(let e=0;e\n\t\t\t
    \n\t\t\t\t

    \n\t\t\t\t\t${t}\n\t\t\t\t\t${a>=0?"+"+a:a}\n\t\t\t\t

    \n\t\t\t
    \n\t\t`)}g.querySelector(".dice-tooltip").insertAdjacentHTML("beforeend",f),g.childNodes[1].classList.add("hidden");const b=l.critical||20,v=l.fumble||1,T=u.parts[0].total;if(T>=b&&(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=new RegExp(Die.rgx.die,"g"),s=e.dataset.formula.replace(a,(e,t,a,s)=>(game.settings.get("mess","max-critical")?s=" + "+t*a+(s||""):(t*=2,s=s||""),t+"d"+a+s));e.innerHTML=' '+s,e.dataset.formula=s})),T<=v&&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"]),!t.getFlag("mess","isBonusDamage"))for(let a of e.items){if(a.id===t.id)continue;let e=[];if(a.getFlag("mess","isBonusDamage")&&a.hasDamage){const t=a.data.data;e.push(t.damage.parts[0][0]);var i=a.name;t.damage.parts[0][1].length>0&&(i+=" - "+game.i18n.localize("DND5E.Damage"+CONFIG.DND5E.damageTypes[t.damage.parts[0][1]])),e.push(i)}e.length>0&&r.parts.push(e)}if("spell"===t.data.type)if("cantrip"===n.scaling.mode){let a=[r.parts[0][0]];const i="character"===e.data.type?s.details.level:s.details.spellLevel;t._scaleCantripDamage(a,i,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)&&(r.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"));let a=t._evalParentheticalTerms(t.formula).map(e=>e instanceof Roll?(e.roll(),e.total):e);e.push(a.join(""))}return r}async function c(e){const t=e.currentTarget.parentNode.querySelector("#context-menu");t&&t.remove();const a=e.currentTarget;a.disabled=!0;const s=a.closest(".chat-card"),n=s.closest(".message").dataset.messageId;if(n){const e=game.messages.get(n);if(!e.owner&&!e.isAuthor)return ui.notifications.error("You do not own the permissions to make that roll!"),!1}const r=a.dataset.formula;let i=new Roll(r);i.roll();let o=document.createElement("div");o.classList.add("dice-roll"),o.classList.add("mess-dice-result"),game.user.isGM&&o.classList.add("mess-gm-dice"),a.nextElementSibling.innerText===game.i18n.localize("DND5E.Versatile")&&o.classList.add("mess-versatile");const l=o.appendChild(document.createElement("span"));l.classList.add("mess-roll-container"),l.innerHTML=`${i.total}`,o.insertAdjacentHTML("beforeend",await i.getTooltip());let c="";const d=a.dataset.terms.split(/(?=[+-])/),m=r.split(/(?=[+-])/).filter(e=>"+"!==e&&"-"!==e);for(let e=0;e\n\t\t\t
    \n\t\t\t\t

    \n\t\t\t\t\t${t}\n\t\t\t\t\t${a>=0?"+"+a:a}\n\t\t\t\t

    \n\t\t\t
    \n\t\t`)}if(o.querySelector(".dice-tooltip").insertAdjacentHTML("beforeend",c),o.childNodes[1].classList.add("hidden"),e.currentTarget.parentNode.replaceChild(o,e.currentTarget),n){game.messages.get(n).update({content:s.parentNode.innerHTML})}}},"./src/scripts/rolls/index.js": /*!************************************!*\ !*** ./src/scripts/rolls/index.js ***! \************************************/ -/*! exports provided: rolling */function(t,e,s){"use strict";s.r(e),s.d(e,"rolling",(function(){return i}));var a=s(/*! ./controls.js */"./src/scripts/rolls/controls.js"),n=s(/*! ./apply-dmg.js */"./src/scripts/rolls/apply-dmg.js"),r=s(/*! ./modify-rolling.js */"./src/scripts/rolls/modify-rolling.js");function i(){game.settings.get("mess","modify-rolling")&&(Object(a.default)(),Object(n.default)(),Object(r.default)())}},"./src/scripts/rolls/modify-rolling.js": +/*! exports provided: rolling */function(e,t,a){"use strict";a.r(t),a.d(t,"rolling",(function(){return i}));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 i(){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(t,e,s){"use strict";s.r(e);var a=s(/*! ./dice.js */"./src/scripts/rolls/dice.js");function n(t){const e=ChatMessage.getSpeaker();let s;e.token&&(s=game.actors.tokens[e.token]),s||(s=game.actors.get(e.actor));const a=s?s.items.filter(e=>e.name===t):[];if(a.length>1)ui.notifications.warn(`Your controlled Actor ${s.name} has more than one Item with name ${t}. The first matched item will be chosen.`);else if(0===a.length)return ui.notifications.warn("Your controlled Actor does not have an item named "+t);const n=a[0],r=!n.getFlag("mess","isBonusDamage");return n.setFlag("mess","isBonusDamage",r),r}async function r(t){const e=document.createElement("div");e.insertAdjacentHTML("afterbegin",t.content);let s=e.querySelector('button[data-action="attack"]');s||(s=e.querySelector('button[data-action="damage"]')),s&&o({currentTarget:s})}function i(t,e){let s=Array.from(t.matchAll(/@RollTable\[([^\]])+\](?:\{([^\}]+)\})?/g));if(s){const e=game.tables;for(let a of s){let s;const n=a[0].match(/\[[a-zA-Z0-9]{16}\]/);if(n)s=e.get(n[0].slice(1,-1));else{const t=a[0].match(/\[([^\]])+\]/)[0].slice(1,-1);s=e.entities.find(e=>e.data.name===t)}let r=s.roll();t=t.replace(a[0],r.results.map(t=>t.text).join(","))}}return t.replace(/\[target\.name\]/g,e.data.name)}async function o(t){"click"===t.type&&(t.preventDefault(),t.stopPropagation());const e=t.currentTarget;e.disabled=!0;const s=e.closest(".chat-card"),n=CONFIG.Item.entityClass._getChatCardActor(s);if(!n||!n.owner)return;const r=n.getOwnedItem(s.dataset.itemId);if(!r)return ui.notifications.error(`The requested item ${s.dataset.itemId} no longer exists on Actor ${n.name}`);let o=game.user.targets;const c=Object.keys(CONFIG.DND5E.areaTargetTypes).includes(getProperty(r,"data.data.target.type"));o.size&&!c||(o=[{data:{name:"someone",img:""}}]);const d=parseInt(s.dataset.spellLevel)||null,m={actor:n,item:r,toHit:await Object(a.getToHitData)({actor:n,item:r}),dmgs:await Object(a.getDmgData)({actor:n,item:r,spellLevel:d}),sceneId:canvas.scene.id,user:game.user.id},g=game.settings.get("mess",game.userId+".autoroll-selector");let u=game.settings.get("core","rollMode");for(const t of o){const e=await r._handleResourceConsumption({isCard:!1,isAttack:!0}),s={...m,target:t.data,flavor:i(r.data.data.chatFlavor,t),allowed:e};let a=await renderTemplate("modules/mess/templates/attack-card.html",s);(g.hit||g.dmg)&&(a=await l(g,a));let n={user:game.user._id,type:CONST.CHAT_MESSAGE_TYPES.OTHER,content:a,speaker:{actor:r.actor._id,token:r.actor.token,alias:r.actor.name}};["gmroll","blindroll"].includes(u)&&(n.whisper=ChatMessage.getWhisperIDs("GM")),"blindroll"===u&&(n.blind=!0),ChatMessage.create(n)}e.disabled=!1}async function l(t,e){let s=document.createElement("div");if(s.classList.add("message"),s.insertAdjacentHTML("afterbegin",e),t.hit){let t=s.querySelector(".mess-button-to-hit");t&&await Object(a.rollToHit)({currentTarget:t})}if(t.dmg){const t=Array.from(s.querySelectorAll(".mess-button-dmg"));for(const e of t)await Object(a.rollDmg)({currentTarget:e})}return s.innerHTML}async function c(t,e,s){const n=e[0].querySelectorAll(".ability-mod, .ability-name");$(n).off(),n.forEach(e=>e.addEventListener("click",(function(e){e.stopPropagation(),e.preventDefault();const s=e.currentTarget.closest(".ability").dataset.ability,n=CONFIG.DND5E.abilities[s],r=["@mod"],i={mod:t.object.data.data.abilities[s].mod},o=t.object.data.flags.dnd5e||{},l=getProperty(t.object,"data.data");o.remarkableAthlete&&DND5E.characterFlags.remarkableAthlete.abilities.includes(s)?(r.push("@remarkable-athlete"),i["remarkable-athlete"]=Math.ceil(.5*l.attributes.prof)):o.jackOfAllTrades&&(r.push("@jack-of-all-trades"),i["jack-of-all-trades"]=Math.floor(.5*l.attributes.prof));let c=getProperty(t.object.data.data.bonuses,"abilities.check");return c&&(r.push("@checkBonus"),i.checkBonus=c),i.parts=r,i.title=game.i18n.format("DND5E.AbilityPromptTitle",{ability:n}),a.rollD20.bind(t.object)(i),!0}))),e[0].querySelectorAll(".ability-save").forEach(e=>e.addEventListener("click",(function(e){e.stopPropagation(),e.preventDefault();const s=e.currentTarget.closest(".ability").dataset.ability,n=CONFIG.DND5E.abilities[s],r=t.object.data.data.abilities[s],i=["@mod"],o={mod:r.mod};r.prof>0&&(i.push("@prof"),o.prof=r.prof);const l=getProperty(t.object.data.data.bonuses,"abilities.save");l&&(i.push("@saveBonus"),o.saveBonus=l),o.title=game.i18n.format("DND5E.SavePromptTitle",{ability:n}),o.parts=i,a.rollD20.bind(t.object)(o)})));const r=e[0].querySelectorAll(".skill-name");$(r).off(),r.forEach(e=>e.addEventListener("click",(function(e){e.stopPropagation(),e.preventDefault();const s=e.currentTarget.closest(".skill").dataset.skill,n=t.object.data.data.skills[s],r=["@mod"],i={mod:n.mod+n.prof};return n.bonus&&(i.skillBonus=n.bonus,r.push("@skillBonus")),i.reliableTalent=n.value>=1&&t.object.getFlag("dnd5e","reliableTalent"),i.parts=r,i.title=game.i18n.format("DND5E.SkillPromptTitle",{skill:CONFIG.DND5E.skills[s]}),a.rollD20.bind(t.object)(i),!1})))}function d(t){t||(t=$(document.getElementById("chat-log"))),t.on("click",".card-buttons button",m.bind(this)),t.on("click",".item-name",this._onChatCardToggleContent.bind(this)),t.on("mouseenter",".mess-chat-target",g),t.on("mouseleave",".mess-chat-target",u),t.on("dblclick",".mess-chat-target",p),t.on("click",".mess-button-to-hit",a.rollToHit),t.on("click",".mess-button-dmg",a.rollDmg)}async function m(t){return t.preventDefault(),t.stopPropagation(),"attack"===t.currentTarget.dataset.action||"damage"===t.currentTarget.dataset.action?o(t):t.currentTarget.dataset.placeTemplate?renderTemplate(t):this._onChatCardAction(t)}async function g(t){t.preventDefault(),t.stopPropagation();const e=await f(t);if(!e)return!1;e._onHoverIn()}async function u(t){t.preventDefault(),t.stopPropagation();const e=await f(t);if(!e||!e.visible)return!1;e._onHoverOut()}async function p(t){t.preventDefault(),t.stopPropagation();const e=await f(t);if(!e||!e.visible)return!1;const s=e.center;canvas.animatePan({x:s.x,y:s.y})}async function f(t){const e=t.currentTarget.closest(".mess-attack-card");if(e.dataset.sceneId!==canvas.scene.id)return!1;const s=e.dataset.targetId;if(!s)return!1;const a=canvas.tokens.placeables.find(t=>t.id===s);return a||!1}e.default=function(){CONFIG.Item.entityClass.chatListeners=d.bind(CONFIG.Item.entityClass),Hooks.on("preCreateChatMessage",r),Hooks.on("renderActorSheet",c),Hooks.on("renderItemSheet",(t,e,s)=>{let a=document.createElement("div");a.classList.add("form-group"),a.appendChild(document.createElement("label")).innerText=game.i18n.localize("MESS.itemSheet.bonusDmg");let n=a.appendChild(document.createElement("div"));n.classList.add("form-fields");let r=n.appendChild(document.createElement("input"));r.type="checkbox",r.name="flags.mess.isBonusDamage",r.checked=t.object.getFlag("mess","isBonusDamage");const i=e[0].querySelector('[name="data.formula"]');i&&i.closest(".form-group").after(a)}),game.mess.toggleItemBonusDamage=n}},"./src/scripts/settings.js": +/*! exports provided: default */function(e,t,a){"use strict";a.r(t);var s=a(/*! ./dice.js */"./src/scripts/rolls/dice.js");function n(e){const t=ChatMessage.getSpeaker();let a;t.token&&(a=game.actors.tokens[t.token]),a||(a=game.actors.get(t.actor));const s=a?a.items.filter(t=>t.name===e):[];if(s.length>1)ui.notifications.warn(`Your controlled Actor ${a.name} has more than one Item with name ${e}. The first matched item will be chosen.`);else if(0===s.length)return ui.notifications.warn("Your controlled Actor does not have an item named "+e);const n=s[0],r=!n.getFlag("mess","isBonusDamage");return n.setFlag("mess","isBonusDamage",r),r}async function r(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&&o({currentTarget:a})}function i(e,t){let a=Array.from(e.matchAll(/@RollTable\[([^\]])+\](?:\{([^\}]+)\})?/g));if(a){const t=game.tables;for(let s of a){let a;const n=s[0].match(/\[[a-zA-Z0-9]{16}\]/);if(n)a=t.get(n[0].slice(1,-1));else{const e=s[0].match(/\[([^\]])+\]/)[0].slice(1,-1);a=t.entities.find(t=>t.data.name===e)}let r=a.roll();e=e.replace(s[0],r.results.map(e=>e.text).join(","))}}return e.replace(/\[target\.name\]/g,t.data.name)}async function o(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 o=game.user.targets;const c=Object.keys(CONFIG.DND5E.areaTargetTypes).includes(getProperty(r,"data.data.target.type"));o.size&&!c||(o=[{data:{name:"someone",img:""}}]);const d=parseInt(a.dataset.spellLevel)||null,m={actor:n,item:r,toHit:await Object(s.getToHitData)({actor:n,item:r}),dmgs:await Object(s.getDmgData)({actor:n,item:r,spellLevel:d}),sceneId:canvas.scene.id,user:game.user.id,isGM:game.user.isGM},u=game.settings.get("mess",game.userId+".autoroll-selector");for(const e of o){const t=await r._handleResourceConsumption({isCard:!1,isAttack:!0}),a=e.actor?e.actor.data:null;let s={};if(a){s.ac={label:'',title:game.i18n.localize("DND5E.ArmorClass"),value:a.data.attributes.ac.value},console.log(a.data.traits);let e=a.data.traits.di.value.filter(e=>"custom"!==e).map(e=>game.i18n.localize("DND5E.Damage"+e[0].toUpperCase()+e.substring(1)));a.data.traits.di.custom&&e.push(a.data.traits.di.custom),s.di={label:'',title:game.i18n.localize("DND5E.DamImm"),value:e.join(", ")};let t=a.data.traits.dr.value.filter(e=>"custom"!==e).map(e=>game.i18n.localize("DND5E.Damage"+e[0].toUpperCase()+e.substring(1)));console.log(t),a.data.traits.dr.custom&&t.push(a.data.traits.dr.custom),s.dr={label:'',title:game.i18n.localize("DND5E.DamRes"),value:t.join(", ")};let n=a.data.traits.dv.value.filter(e=>"custom"!==e).map(e=>game.i18n.localize("DND5E.Damage"+e[0].toUpperCase()+e.substring(1)));a.data.traits.dv.custom&&n.push(a.data.traits.dv.custom),s.dv={label:'',title:game.i18n.localize("DND5E.DamVuln"),value:n.join(", ")}}const n={...m,target:e.data,targetData:s,flavor:i(r.data.data.chatFlavor,e),allowed:t};let o=await renderTemplate("modules/mess/templates/attack-card.html",n);(u.hit||u.dmg)&&(o=await l(u,o));let c={user:game.user._id,type:CONST.CHAT_MESSAGE_TYPES.OTHER,content:o,speaker:{actor:r.actor._id,token:r.actor.token,alias:r.actor.name}};if(!game.user.isGM||!game.settings.get("mess","attack-card-always-public")){const e=game.settings.get("core","rollMode");["gmroll","blindroll"].includes(e)&&(c.whisper=ChatMessage.getWhisperRecipients("GM")),"selfroll"===e&&(c.whisper=[game.user.id])}ChatMessage.create(c)}t.disabled=!1}async function l(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 c(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"],i={mod:e.object.data.data.abilities[a].mod},o=e.object.data.flags.dnd5e||{},l=getProperty(e.object,"data.data");o.remarkableAthlete&&DND5E.characterFlags.remarkableAthlete.abilities.includes(a)?(r.push("@remarkable-athlete"),i["remarkable-athlete"]=Math.ceil(.5*l.attributes.prof)):o.jackOfAllTrades&&(r.push("@jack-of-all-trades"),i["jack-of-all-trades"]=Math.floor(.5*l.attributes.prof));let c=getProperty(e.object.data.data.bonuses,"abilities.check");return c&&(r.push("@checkBonus"),i.checkBonus=c),i.parts=r,i.title=game.i18n.format("DND5E.AbilityPromptTitle",{ability:n}),s.rollD20.bind(e.object)(i),!1})));const r=t[0].querySelectorAll(".ability-save");$(r).off(),r.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],i=["@mod"],o={mod:r.mod};r.prof>0&&(i.push("@prof"),o.prof=r.prof);const l=getProperty(e.object.data.data.bonuses,"abilities.save");return l&&(i.push("@saveBonus"),o.saveBonus=l),o.title=game.i18n.format("DND5E.SavePromptTitle",{ability:n}),o.parts=i,s.rollD20.bind(e.object)(o),!1})));const i=t[0].querySelectorAll(".skill-name");$(i).off(),i.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"],i={mod:n.mod+n.prof};return n.bonus&&(i.skillBonus=n.bonus,r.push("@skillBonus")),i.reliableTalent=n.value>=1&&e.object.getFlag("dnd5e","reliableTalent"),i.parts=r,i.title=game.i18n.format("DND5E.SkillPromptTitle",{skill:CONFIG.DND5E.skills[a]}),s.rollD20.bind(e.object)(i),!1})))}function d(e){e||(e=$(document.getElementById("chat-log"))),e.on("click",".card-buttons button",m.bind(this)),e.on("click",".item-name",this._onChatCardToggleContent.bind(this)),e.on("mouseenter",".mess-chat-target",u),e.on("mouseleave",".mess-chat-target",g),e.on("dblclick",".mess-chat-target",p),e.on("click",".mess-button-to-hit",s.rollToHit),e.on("click",".mess-button-dmg",s.rollDmg),e.on("click",".mess-show-btn",h)}async function m(e){return e.preventDefault(),e.stopPropagation(),"attack"===e.currentTarget.dataset.action||"damage"===e.currentTarget.dataset.action?o(e):e.currentTarget.dataset.placeTemplate?renderTemplate(e):this._onChatCardAction(e)}async function u(e){e.preventDefault(),e.stopPropagation();const t=await f(e);if(!t)return!1;t._onHoverIn()}async function g(e){e.preventDefault(),e.stopPropagation();const t=await f(e);if(!t||!t.visible)return!1;t._onHoverOut()}async function p(e){e.preventDefault(),e.stopPropagation();const t=await f(e);if(!t||!t.visible)return!1;const a=t.center;canvas.animatePan({x:a.x,y:a.y})}async function f(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 h(e){e.currentTarget.closest(".mess-chat-to-hit, .mess-chat-dmg").classList.toggle("mess-show-players");const t=e.currentTarget.closest(".mess-attack-card"),a=t.closest(".message").dataset.messageId;game.messages.get(a).update({content:t.parentNode.innerHTML})}t.default=function(){CONFIG.Item.entityClass.chatListeners=d.bind(CONFIG.Item.entityClass),Hooks.on("preCreateChatMessage",r),Hooks.on("renderActorSheet",c),Hooks.on("renderItemSheet",(e,t,a)=>{let s=document.createElement("div");s.classList.add("form-group"),s.appendChild(document.createElement("label")).innerText=game.i18n.localize("MESS.itemSheet.bonusDmg");let n=s.appendChild(document.createElement("div"));n.classList.add("form-fields");let r=n.appendChild(document.createElement("input"));r.type="checkbox",r.name="flags.mess.isBonusDamage",r.checked=e.object.getFlag("mess","isBonusDamage");const i=t[0].querySelector('[name="data.formula"]');i&&i.closest(".form-group").after(s)}),game.mess.toggleItemBonusDamage=n;const e=Roll.prototype.getTooltip;Roll.prototype.getTooltip=function(){for(let t=0;t${game.i18n.localize("MESS.reloadReminder.text")}

    `,buttons:{yes:{icon:'',label:game.i18n.localize("MESS.reloadReminder.yes"),callback:()=>location.reload()},no:{icon:'',label:game.i18n.localize("MESS.reloadReminder.no")}}}).render(!0)}}}}); +/*! 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:"world",config:!1,default:e,type:Boolean}),game.settings.register("mess","better-draggable",{name:"Activate better drag'n'drop workflow.",scope:"world",config:!1,default:e,type:Boolean}),game.settings.register("mess","better-draggable-lists",{name:"Activate better drag'n'drop workflow for lists.",scope:"world",config:!1,default:!0,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:"world",config:!1,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:"world",config:!1,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:!1,default:e,type:Boolean}),game.settings.register("mess","attack-card-always-public",{name:"Roll mode for alternative rolling.",hint:"Always roll attack rolls public, with hidden rolls, but visible target.",scope:"world",config:!1,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:!1,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:!1,default:!0,type:Boolean}),game.settings.registerMenu("mess","templateTexture",{name:game.i18n.localize("MESS.FVTTSettings.description"),label:game.i18n.localize("MESS.FVTTSettings.button"),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}),game.settings.register("mess","max-critical",{name:"Activate maximum critical rolls.",hint:"Changes behaviour of critical damage rolls to maximize the damage of the extra dice for criticals!",scope:"world",config:!1,default:!1,type:Boolean}),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",config:!1,default:!0,type:Object}),game.settings.register("mess","templateAutoTargeting",{scope:"world",default:!0,type:Boolean}),game.settings.register("mess","templateDrawBordersOnlyOnHighlight",{scope:"world",default:!0,type:Boolean}),this.loadTemplates()}static loadTemplates(){loadTemplates(["modules/mess/templates/settings/templates.html","modules/mess/templates/settings/dnd5e.html","modules/mess/templates/settings/misc.html"])}static get defaultOptions(){return{...super.defaultOptions,template:"modules/mess/templates/settings/settings.html",height:"auto",title:"Mess - Moerills enhancing super-suit(e) - Settings Menu",width:600,classes:["mess","settings"],tabs:[{navSelector:".tabs",contentSelector:"form",initial:"name"}],submitOnClose:!0}}constructor(e={},t){super(e,t),this.object=game.settings.get("mess","templateTexture")}_getHeaderButtons(){let e=super._getHeaderButtons();return e[0].label="Save & Close",e}getSettingsData(){const e="dnd5e"===game.system.id;let t={"modify-templates":game.settings.get("mess","modify-templates"),"change-placeables":game.settings.get("mess","change-placeables"),templateTexture:game.settings.get("mess","templateTexture"),templateAutoTargeting:game.settings.get("mess","templateAutoTargeting"),templateDrawBordersOnlyOnHighlight:game.settings.get("mess","templateDrawBordersOnlyOnHighlight"),"better-draggable-lists":game.settings.get("mess","better-draggable-lists")};return e&&(t.templateTexture=game.settings.get("mess","templateTexture"),t["modify-rolling"]=game.settings.get("mess","modify-rolling"),t["max-critical"]=game.settings.get("mess","max-critical"),t["actor-item-sort"]=game.settings.get("mess","actor-item-sort"),t["prepared-spell-tracker"]=game.settings.get("mess","prepared-spell-tracker"),t["add-scrolling"]=game.settings.get("mess","add-scrolling"),t["attack-card-always-public"]=game.settings.get("mess","attack-card-always-public")),t}getData(){const e="dnd5e"===game.system.id;let t=super.getData();return e&&(t.dmgTypes=CONFIG.DND5E.damageTypes,t.templateTypes=CONFIG.MeasuredTemplate.types),t.isDnD=e,t.settings=this.getSettingsData(),t}activateListeners(e){super.activateListeners(e)}_updateObject(e,t){const a=expandObject(t);for(let[e,t]of Object.entries(a))game.settings.set("mess",e,t);new Dialog({content:`

    ${game.i18n.localize("MESS.reloadReminder.text")}

    `,buttons:{yes:{icon:'',label:game.i18n.localize("MESS.reloadReminder.yes"),callback:()=>location.reload()},no:{icon:'',label:game.i18n.localize("MESS.reloadReminder.no")}}}).render(!0)}}}}); //# 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 89d1fbe..7932126 100644 --- a/dist/scripts/index.js.map +++ b/dist/scripts/index.js.map @@ -1 +1 @@ -{"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/chat-popup/index.js","webpack:///./src/scripts/draggable-lists/draggable-list.js","webpack:///./src/scripts/draggable-lists/index.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","initChatPopUp","getElementById","contains","log","DraggableList","container","selector","mergeObject","defaultOptions","_init","offset","time","onDragStart","onDragEnd","onDrop","_items","Array","from","childNodes","filter","matches","rect","getBoundingClientRect","insideChild","clientY","height","clientX","width","_resetOffsets","_over","writable","idx","_initItem","position","_onDragEnterItem","_onDragLeaveItem","srcElement","opacity","TweenLite","_dragged","to","resetList","top","offsetList","initDraggableLists","oldFun","SidebarDirectory","activateListeners","game","settings","CONFIG","debug","mess","sheet","render","user","isGM","moduleVersion","version","register","default","String","scope","oldVersion","isNewerVersion","init","changeTemplates","newFun","MeasuredTemplate","toString","replace","querySelector","Function","getTargets","isTokenInside","ret","dndTemplateSettings","system","id","importedJS","import","AbilityTemplate","itemHook","_originalFromItem","fromItem","item","template","path","getFlag","hasDamage","damage","parts","loadTexture","then","tex","texture","activatePreviewListeners","token","scene","templatePos","startX","startY","currGrid","shape","tokens","getEmbeddedCollection","targets","updateTokenTargets","div","innerText","i18n","localize","formField","inp","dtype","insertAdjacentHTML","button","_activateFilePicker","after","addPreparedSpellTracker","isNPC","tracker","border","preparedSpells","sep","val","isNaN","setFlag","constructor","alignSelf","parentNode","insertBefore","modifyApplyDmg","setProperty","canApply","li","controlled","find","condition","chatLogHook","advSelector","userId","templateData","advantage","normal","disadvantage","renderTemplate","arr","currIdx","findIndex","remove","newSelected","set","toggle","controls","hit","dmg","getD20Modifier","getAdvantageSettings","rollD20","adv","nd","mods","halflingLucky","elvenAccuracy","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","rgx","RegExp","Die","die","match","replaceChild","getDmgData","spellLevel","level","versatile","splice","itm","bnsDmgParts","itmData","lbl","DND5E","damageTypes","scaling","newDmgPart","lvl","details","_scaleCantripDamage","_scaleSpellDamage","part","_evalParentheticalTerms","rollDmg","rolling","toggleItemBonusDamage","itemName","getSpeaker","actors","warn","newState","preCreateChatMessageHook","renderAttack","getFlavor","chatFlavor","rollTables","matchAll","collection","tables","tableData","table","entities","result","results","text","areaSkill","keys","areaTargetTypes","getProperty","size","img","attackData","toHit","dmgs","sceneId","autoroll","allowed","_handleResourceConsumption","isCard","isAttack","attackTemplateData","flavor","autoRoll","toHitBtn","btns","actorSheetHook","abilityMods","off","abilityId","ability","label","abilities","mod","feats","remarkableAthlete","characterFlags","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","checked","MessSettings","FormApplication","isDnD","hint","config","Boolean","registerMenu","icon","restricted","loadTemplates","super","classes","tabs","navSelector","contentSelector","initial","submitOnClose","_getHeaderButtons","getData","dmgTypes","templateTypes","types","getSettingsData","formData","expandObject","entries","Dialog","buttons","yes","callback","location","reload","no"],"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}","export function initChatPopUp() {\n\tHooks.on('renderChatMessage', (app, html, options) => {\n\t\tif (document.getElementById('chat').classList.contains('active')) return;\n\n\n\t\tconsole.log('rendering chat message');\n\t\tconsole.log(app, html, options);\n\t\tconsole.log(document.getElementById('chat').classList.contains('active'))\n\t});\n\n\tconst hud = document.getElementById('hud');\n\n\tlet popupDiv = hud.appendChild(document.createElement('div'));\n\tpopupDiv.classList.add('mess-chat-popup')\n}","export class DraggableList {\n\tconstructor(container, selector, options = {}) {\n\t\tthis.container = container;\n\t\tthis.selector = selector;\n\t\tthis.options = mergeObject(this.defaultOptions, options);\n\n\t\tthis._init();\n\t}\n\t\n\tget defaultOptions() {\n\t\treturn {\n\t\t\toffset: 21, // in px\n\t\t\ttime: 0.1, // in seconds\n\t\t\t// folowing are not used\n\t\t\tonDragStart: null,\n\t\t\tonDragEnd: null,\n\t\t\tonDrop: null,\n\t\t\t\n\t\t}\n\t}\n\n\tasync _init() {\n\t\tthis._items = Array.from(this.container.childNodes).filter(e => e.matches && e.matches(this.selector));\n\t\tthis.container.addEventListener('dragleave', ev => {\n\t\t\tconst rect = this.container.getBoundingClientRect();\n\t\t\tconst boundary = 5;\n\t\t\t// Reset offsets, when first target was inside a child container.\n\t\t\tconst insideChild = ev.insideChild;\n\t\t\tif (!insideChild \n\t\t\t\t\t&& ev.clientY < rect.y + rect.height - boundary\n\t\t\t\t\t&& ev.clientY > rect.y + boundary\n\t\t\t\t\t&& ev.clientX > rect.x + boundary \n\t\t\t\t\t&& ev.clientX < rect.x - boundary + rect.width) {\n\t\t\t\t\t\tev.insideChild = true;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\tthis._resetOffsets();\n\t\t});\n\n /** Possibly modifying the drop target.\n\t * Why?\n\t * Due to the method used, the target gets moved out of the way and it will automatically drop onto the container element.\n\t * Most drop functions in fvtt just append to the end though, when dropping onto the container.\n\t * => We search for the first offset element and define that one as target. Since default for sorting is \"insertBefore\", and most implementations i've found build upon FVTT default/dnd5e systems default, this is a rather save implementation to find the real target.\n\t */\n\t\tthis.container.addEventListener('drop', ev => {\n\t\t\tconst insideChild = ev.insideChild;\n\t\t\tif (this._over && !insideChild) {\n\t\t\t\tObject.defineProperty(ev, 'target', {writable: false, value: this._over})\n\t\t\t\t// Make sure that outer directories are not overwriting this stuff!\n\t\t\t\tev.insideChild = true;\n\t\t\t}\n\t\t});\n\t\t\n\t\tthis._items.forEach((e, idx) => this._initItem(e, idx))\n\t}\n\n\tasync _initItem(el, idx) {\n\t\tel.style.position = \"relative\";\n\t\tel.addEventListener('dragenter', ev => this._onDragEnterItem(ev, idx));\n\t\tel.addEventListener('dragleave', ev => this._onDragLeaveItem(ev, idx));\n\t\tel.addEventListener('dragend', ev => {\n\t\t\t// safety net if for some reasons the rerender is to slow or fails...\n\t\t\tconst srcElement = ev.currentTarget;\n\t\t\tsrcElement.style.opacity = null;\n\t\t\tsrcElement.style.height = null;\n\t\t\tTweenLite.from(srcElement, this.options.time, {opacity:0, height:0});\n\t\t\tthis._resetOffsets();\n\t\t});\n\t\tel.addEventListener('dragstart', ev => {\n\t\t\tthis._dragged = ev.currentTarget;\n\t\t\tTweenLite.to(ev.currentTarget, this.options.time, {opacity: 0, height: 0});\n\t\t})\n\t}\n\n\t_onDragEnterItem(ev, idx) {\n\t\tev.stopPropagation();\n\t\t// save the current target\n\t\tthis._over = this._items[idx];\n\n\t\tconst time = this.options.time;\n\t\tconst offset = this.options.offset;\n\t\tconst resetList = this._items.slice(0, idx);\n\t\tTweenLite.to(resetList, time, {top: 0});\n\t\tconst offsetList = this._items.slice(idx);\n\t\tTweenLite.to(offsetList, time, {top: offset});\n\n\t\treturn false;\n\t}\n\n\n\t_onDragLeaveItem(ev, idx) {\n\t\t// Empty for now?\n\t}\n\n\t_resetOffsets(time = this.options.time) {\n\t\tTweenLite.to(this._items, time, {top: 0});\n\t}\n}\n","import {DraggableList} from './draggable-list';\n\nexport function initDraggableLists() {\n // make sure my listeners to get bound _before_ the class owns get bound to the elements\n // Only needed for the \"root\" directory though.. :/\n const oldFun = SidebarDirectory.prototype.activateListeners;\n SidebarDirectory.prototype.activateListeners = function(html) {\n const list = html[0].querySelectorAll('.directory-list, .subdirectory');\n list.forEach(e => new DraggableList(e, '.entity'));\n oldFun.call(this, html);\n }\n\n // this does work, since on default most sheets have their drop function bound to the form, not the item list!\n Hooks.on('renderActorSheet', (app, html, options) => {\n const list = html[0].querySelectorAll('.item-list');\n list.forEach(e => new DraggableList(e, '.item'));\n });\n}","import { rolling } from './rolls';\nimport { MessSettings } from './settings.js';\nimport {dndTemplateSettings, changeTemplates } 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\nimport {initDraggableLists} from './draggable-lists';\nimport {initChatPopUp} from './chat-popup';\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\t// initChatPopUp();\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\tgame.mess = {};\n\tCONFIG.debug.mess = false;\n\tMessSettings.init();\n\n\trolling();\n\n\tdndTemplateSettings();\n\tchangeTemplates();\n\tif (game.settings.get('mess', 'change-placeables'))\n\t\tchangePlaceables();\n\tif (game.settings.get('mess', 'better-draggable-lists'))\n\t\tinitDraggableLists();\n});\n","export function changeTemplates() {\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 \n\tlet newFun = MeasuredTemplate.prototype.refresh.toString();\n\n\tif (game.settings.get('mess', 'modify-templates')) {\n\t\tnewFun = newFun.replace(/this\\.template\\.beginTextureFill\\(\\{[\\s\\S]*\\}\\)\\;/, `\n\t\t\t{\n\t\t\t\tlet mat = PIXI.Matrix.IDENTITY;\n\t\t\t\t// rectangle\n\t\t\t\tif (this.shape.width && this.shape.height)\n\t\t\t\t\tmat.scale(this.shape.width / this.texture.width, this.shape.height / this.texture.height);\n\t\t\t\telse if (this.shape.radius) {\n\t\t\t\t\tmat.scale(this.shape.radius * 2 / this.texture.height, this.shape.radius * 2 / this.texture.width)\n\t\t\t\t\t// Circle center is texture start...\n\t\t\t\t\tmat.translate(-this.shape.radius, -this.shape.radius);\n\t\t\t\t} else if (this.data.t === \"ray\") {\n\t\t\t\t\tconst d = canvas.dimensions,\n\t\t\t\t\t\t\t\theight = this.data.width * d.size / d.distance,\n\t\t\t\t\t\t\t\twidth = this.data.distance * d.size / d.distance;\n\t\t\t\t\tmat.scale(width / this.texture.width, height / this.texture.height);\n\t\t\t\t\tmat.translate(0, -height * 0.5);\n\n\t\t\t\t\tmat.rotate(toRadians(this.data.direction));\n\t\t\t\t} else {// cone\n\t\t\t\t\tconst d = canvas.dimensions;\n\t\t\t\n\t\t\t\t\t// Extract and prepare data\n\t\t\t\t\tlet {direction, distance, angle} = this.data;\n\t\t\t\t\tdistance *= (d.size / d.distance);\n\t\t\t\t\tdirection = toRadians(direction);\n\t\t\t\t\tconst width = this.data.distance * d.size / d.distance;\n\n\t\t\t\t\tconst angles = [(angle/-2), (angle/2)];\n\t\t\t\t\tdistance = distance / Math.cos(toRadians(angle/2));\n\t\t\t\n\t\t\t\t\t// Get the cone shape as a polygon\n\t\t\t\t\tconst rays = angles.map(a => Ray.fromAngle(0, 0, direction + toRadians(a), distance+1));\n\t\t\t\t\tconst height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x)\n\t\t\t\t\t\t\t\t\t\t\t\t\t+ (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y));\n\t\t\t\t\tmat.scale(width / this.texture.width, height / this.texture.height);\n\t\t\t\t\tmat.translate(0, -height/2)\n\t\t\t\t\tmat.rotate(toRadians(this.data.direction));\n\t\t\t\t}\n\t\t\t\tthis.template.beginTextureFill({\n\t\t\t\t\ttexture: this.texture,\n\t\t\t\t\tmatrix: mat,\n\t\t\t\t\talpha: 0.8\n\t\t\t\t});\n\t\t\t\t// move into draw or so\n\t\t\t\tconst source = getProperty(this.texture, \"baseTexture.resource.source\")\n\t\t\t\tif ( source && (source.tagName === \"VIDEO\") ) {\n\t\t\t\t\tsource.loop = true;\n\t\t\t\t\tsource.muted = true;\n\t\t\t\t\tgame.video.play(source);\n\t\t\t\t}\n\t\t}`);\n\n\t\tHooks.on('renderMeasuredTemplateConfig', (app, html, data) => {\n\t\t\thtml[0].querySelector('.file-picker').dataset.type = 'imagevideo'\n\t\t});\n\t}\n\t\n\tif (game.settings.get('mess', 'templateAutoTargeting')) {\n\t\tnewFun = newFun.replace(/this\\.\\_borderThickness/, \"this.texture && !this._hover ? 0 : this._borderThickness\");\n\t\tnewFun = newFun.replace(/return\\sthis\\;/, `\n\t\t\tconst grid = canvas.grid;\n\t\t\t// only show grid highlights on hover\n\t\t\tif (this.texture) {\n\t\t\t\tconst hl = grid.getHighlightLayer(\\`Template.\\$\\{this.id\\}\\`);\n\t\t\t\tif (hl)\n\t\t\t\t\thl.renderable = this._hover;\n\t\t\t}\n\t\t\treturn this;`);\n\t}\n\n\tMeasuredTemplate.prototype.refresh = Function(`\"use strict\"; return ( function ${newFun} )`)();\n\n\t\n\tif (game.settings.get('mess', 'templateAutoTargeting')) {\n\t\tMeasuredTemplate.prototype.getTargets = getTargets;\n\t\tMeasuredTemplate.prototype.isTokenInside = isTokenInside;\n\n\t\tconst oldFun = MeasuredTemplate.prototype._onDragLeftMove;\n\t\tMeasuredTemplate.prototype._onDragLeftMove = function(ev) {\n\t\t\tconst ret = oldFun.bind(this)(ev);\n\n\t\t\tfor (let c of ev.data.clones)\n\t\t\t\tthis.getTargets(c);\n\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n\nexport async function dndTemplateSettings() {\n if (game.system.id !== 'dnd5e') return;\n\n\tconst importedJS = (await import(/* webpackIgnore: true */ '/systems/dnd5e/module/pixi/ability-template.js'))\n\tconst AbilityTemplate = importedJS.default || importedJS.AbilityTemplate;\n\n\t// Auto texture creation from item\n\tif (game.settings.get('mess', 'modify-templates')) {\n\t\tHooks.on('renderItemSheet', itemHook);\n\t\t\n\t\t\n\t\tconst _originalFromItem = AbilityTemplate.fromItem;\n\t\tAbilityTemplate.fromItem = function(item) {\n\t\t\tconst template = _originalFromItem.bind(this)(item);\n\t\t\t\n\t\t\t// generate a texture based on the items dmg type, ...\n\t\t\t// Add settings to define custom templates for stuff.\n\t\t\tlet path = item.getFlag('mess', 'templateTexture');\n\t\t\tif (!path && item.hasDamage) {\n\t\t\t\tconst settings = game.settings.get('mess', 'templateTexture') || {};\n\t\t\t\tpath = settings[item.data.data.damage.parts[0][1]] || {};\n\t\t\t\tpath = path[template.data.t];\n\t\t\t}\n\t\t\tif (path)\n\t\t\t\tloadTexture(path).then(tex => {\n\t\t\t\t\ttemplate.texture = tex;\n\t\t\t\t\ttemplate.data.texture = path;\n\t\t\t\t\ttemplate.refresh();\n\t\t\t\t})\n\t\t\ttemplate.item = item;\n\t\t\treturn template;\n\t\t}\n\t}\n\n\n\tif (game.settings.get('mess', 'templateAutoTargeting')) {\n\t\t// rather ugly, maybe find a better way at some point :shrug:\n\t\tconst origPrevListeners = AbilityTemplate.prototype.activatePreviewListeners.toString();\n\t\tconst newFun = origPrevListeners.replace(/this\\.refresh\\(\\)\\;/, \n\t\t\t\t\t// get targets\n\t\t\t\t\t\t`this.refresh();\n\t\t\t\t\t\tthis.getTargets(this);\n\t\t\t\t\t`);\n\n\t\tAbilityTemplate.prototype.getTargets = getTargets;\n\t\tAbilityTemplate.prototype.isTokenInside = isTokenInside;\n\n\t\tAbilityTemplate.prototype.activatePreviewListeners = Function(`\"use strict\"; return ( function ${newFun} )`)();\n\t}\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(template) {\n\tconst tokens = canvas.scene.getEmbeddedCollection('Token');\n\tlet targets = [];\n\t\n\tfor (const token of tokens)\n\t\tif (template.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 = game.i18n.localize('MESS.itemSheet.templateTexture');\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\tconst target = html[0].querySelector('[name=\"data.target.units\"]');\n if (target)\n\t\ttarget.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\tif (actor.isNPC) return;\n\t\t\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-detail');\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 = `${game.i18n.localize('MESS.actorSheet.preparedSpellTracker')}: ${data.preparedSpells}`;\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\t\n\t\tif (app.constructor.name === 'Tidy5eSheet') {\n\t\t\tconst el = html[0].querySelector('.spellcasting-ability');\n\t\t\tel.appendChild(tracker, el);\n\t\t} else {\n\t\t\tconst el = html[0].querySelector('.spellbook .inventory-list');\n\t\t\ttracker.style.flex = '0';\n\t\t\ttracker.style.alignSelf = 'flex-start';\n\t\t\ttracker.style.margin = '0 8px';\n\t\t\tel.parentNode.insertBefore(tracker, el);\n\t\t}\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\thtml[0].classList.add('mess');\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}","/**\n * All the functions provided here are heavily based on Foundrys DnD5e system, authored by Atropos.\n * Original repository: https://gitlab.com/foundrynet/dnd5e\n * Original Author: Atropos\n * License: GNU GPLv3\n */\n\nfunction 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 rgx = new RegExp(Die.rgx.die, \"g\");\n\t\t\tconst formula = e.dataset.formula.replace(rgx, (match, nd, d, mods) => {\n\t\t\t\tif (game.settings.get('mess', 'max-critical'))\n\t\t\t\t\t\tmods = \" + \" + nd * d + (mods || \"\");\n\t\t\t\telse {\n\t\t\t\t\t\tnd = nd * 2;\n\t\t\t\t\t\tmods = mods || \"\";\n\t\t\t\t}\n\t\t\t\treturn nd + \"d\" + d + mods;\n\t\t\t});\n\t\t\te.innerHTML = ` ${formula}`;\n\t\t\te.dataset.formula = 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\n\t// Only apply for items that are not bonus dmg themself\n\tif (!item.getFlag('mess', 'isBonusDamage'))\n\t\tfor (let itm of actor.items){\n\t\t\t// skip self\n\t\t\tif (itm.id === item.id) continue;\n\n\t\t\tlet bnsDmgParts = [];\n\t\t\tif (itm.getFlag('mess', 'isBonusDamage')){\n\t\t\t\tif (itm.hasDamage){\n\t\t\t\t\tconst itmData = itm.data.data;\n\t\t\t\t\tbnsDmgParts.push(itmData.damage.parts[0][0]);\n\t\t\t\t\tvar lbl = itm.name;\n\t\t\t\t\tif (itmData.damage.parts[0][1].length > 0) lbl += ` - ${game.i18n.localize('DND5E.Damage' + CONFIG.DND5E.damageTypes[itmData.damage.parts[0][1]])}`;\n\t\t\t\t\tbnsDmgParts.push(lbl);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bnsDmgParts.length > 0) rollData.parts.push(bnsDmgParts);\n\t\t}\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\trollData.parts[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\n\t\t//evalute damage formula's for example \"ceil(@classes.rogue.levels/2))d6\" -> \"4d6\"\n\t\tlet terms = roll._evalParentheticalTerms(roll.formula).map(t => {\n\t\t\tif ( t instanceof Roll ) {\n\t\t\t\tt.roll();\n\t\t\t\treturn t.total;\n\t\t\t}\n\t\t\treturn t;\n\t\t});\n\t\tpart.push(terms.join(''));\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\tgame.mess.toggleItemBonusDamage = toggleItemBonusDamage;\n}\n\n/**\n * Heavily based on: https://gitlab.com/foundrynet/dnd5e/-/blob/master/module/macros.js#L42\n * original author: Atropos\n * source repository: https://gitlab.com/foundrynet/dnd5e\n * license: GPLv3\n * @param {*} itemName \n */\nfunction toggleItemBonusDamage(itemName) {\n\tconst speaker = ChatMessage.getSpeaker();\n let actor;\n\tif ( speaker.token ) \n\t\tactor = game.actors.tokens[speaker.token];\n\tif ( !actor ) \n\t\tactor = game.actors.get(speaker.actor);\n // Get matching items\n const items = actor ? actor.items.filter(i => i.name === itemName) : [];\n if ( items.length > 1 ) {\n ui.notifications.warn(`Your controlled Actor ${actor.name} has more than one Item with name ${itemName}. The first matched item will be chosen.`);\n } else if ( items.length === 0 ) {\n return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`);\n }\n const item = items[0];\n\n\tconst newState = !item.getFlag('mess', 'isBonusDamage');\n\t// toggle bonus dmg\n\titem.setFlag('mess', 'isBonusDamage', newState);\n\treturn newState;\n}\n\n/**\n * Initializes all the hoohks!\n */\nfunction setupHooks() {\n\tCONFIG.Item.entityClass.chatListeners = chatListeners.bind(CONFIG.Item.entityClass);\n\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\t// Hooks.on('ready', chatListeners.bind(CONFIG.Item.entityClass));\n\n\tHooks.on('renderItemSheet', (app, html, data) => {\n\t\tlet div = document.createElement('div');\n\t\tdiv.classList.add('form-group');\n\t\tdiv.appendChild(document.createElement('label')).innerText = game.i18n.localize('MESS.itemSheet.bonusDmg');\n\t\tlet formField = div.appendChild(document.createElement('div'));\n\t\tformField.classList.add('form-fields');\n\t\tlet inp = formField.appendChild(document.createElement('input'));\n\t\tinp.type = 'checkbox';\n\t\tinp.name = 'flags.mess.isBonusDamage';\n\t\tinp.checked = app.object.getFlag('mess', 'isBonusDamage');\n\n\t\tconst target = html[0].querySelector('[name=\"data.formula\"]');\n\t\tif (target)\n\t\t\ttarget.closest('.form-group').after(div);\n\t})\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// TODO: for compendium rolltables\nfunction getFlavor(chatFlavor, target) {\n\tconst rollTableRegExp = /@RollTable\\[([^\\]])+\\](?:\\{([^\\}]+)\\})?/g;\n\tlet rollTables = Array.from(chatFlavor.matchAll(rollTableRegExp));\n\tif (rollTables) {\n\t\tconst collection = game.tables;\n\t\tfor (let tableData of rollTables) {\n\t\t\tlet table;\n\t\t\tconst id = tableData[0].match(/\\[[a-zA-Z0-9]{16}\\]/);\n\t\t\tif (id) {\n\t\t\t\ttable = collection.get(id[0].slice(1, -1));\n\t\t\t} else {\n\t\t\t\tconst name = tableData[0].match(/\\[([^\\]])+\\]/)[0].slice(1, -1);\n\t\t\t\ttable = collection.entities.find(e => e.data.name === name);\n\t\t\t}\n\t\t\tlet result = table.roll();\n\t\t\tchatFlavor = chatFlavor.replace(tableData[0], result.results.map(e => e.text).join(\",\"));\n\t\t}\n\t}\n\treturn chatFlavor.replace(/\\[target\\.name\\]/g, target.data.name)\n}\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: getFlavor(item.data.data.chatFlavor, target),\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\t\t// Add feat-related proficiency bonuses\n\t\tconst actorData = getProperty(app.object, \"data.data\")\n if ( feats.remarkableAthlete && DND5E.characterFlags.remarkableAthlete.abilities.includes(abilityId) ) {\n parts.push(\"@remarkable-athlete\");\n data[\"remarkable-athlete\"] = Math.ceil(0.5 * actorData.attributes.prof);\n }\n else if ( feats.jackOfAllTrades ) {\n parts.push(\"@jack-of-all-trades\");\n data[\"jack-of-all-trades\"] = Math.floor(0.5 * actorData.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\t\tdata.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(html) {\n\tif (!html)\n\t\thtml = $(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\tev.preventDefault(); ev.stopPropagation();\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: \"world\",\n\t\t\tconfig: false,\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: \"world\",\n\t\t\tconfig: false,// Change if implemented\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t})\n\n\t\tgame.settings.register('mess', 'better-draggable-lists', {\n\t\t\tname: \"Activate better drag'n'drop workflow for lists.\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: false,\n\t\t\tdefault: true,\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: \"world\",\n\t\t\tconfig: false,\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: \"world\",\n\t\t\tconfig: false,\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: false,\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: false,\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: false,\n\t\t\tdefault: true,\n\t\t\ttype: Boolean\n\t\t});\t\n\t\tgame.settings.registerMenu('mess', 'templateTexture', {\n\t\t\tname: game.i18n.localize('MESS.FVTTSettings.description'),\n\t\t\tlabel: game.i18n.localize('MESS.FVTTSettings.button'),\n\t\t\ticon: \"fas fa-mug-hot\",\n\t\t\ttype: MessSettings,\n\t\t\trestricted: true\n\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\n\t\tgame.settings.register('mess', 'max-critical', {\n\t\t\tname: \"Activate maximum critical rolls.\",\n\t\t\thint: \"Changes behaviour of critical damage rolls to maximize the damage of the extra dice for criticals!\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: false,\n\t\t\tdefault: false,\n\t\t\ttype: Boolean\n\t\t});\n\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\tconfig: false,\n\t\t\tdefault: true,\n\t\t\ttype: Object\n\t\t});\t\n\n\t\tgame.settings.register('mess', 'templateAutoTargeting', {\n\t\t\tscope: \"world\",\n\t\t\tdefault: true,\n\t\t\ttype: Boolean\n\t\t})\n\n\t\tgame.settings.register('mess', 'templateDrawBordersOnlyOnHighlight', {\n\t\t\tscope: \"world\",\n\t\t\tdefault: true,\n\t\t\ttype: Boolean\n\t\t})\n\n\t\tthis.loadTemplates();\n\t}\n\tstatic loadTemplates() {\n\t\tloadTemplates([\n\t\t\t'modules/mess/templates/settings/templates.html',\n\t\t\t'modules/mess/templates/settings/dnd5e.html',\n\t\t\t'modules/mess/templates/settings/misc.html'\n\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/settings.html\",\n\t\t\theight: \"auto\",\n\t\t\ttitle: \"Mess - Moerills enhancing super-suit(e) - Settings Menu\",\n\t\t\twidth: 600,\n\t\t\tclasses: [\"mess\", \"settings\"],\n\t\t\ttabs: [ \n\t\t\t\t{\n\t\t\t\t\tnavSelector: '.tabs',\n\t\t\t\t\tcontentSelector: 'form',\n\t\t\t\t\tinitial: 'name'\n\t\t\t\t} \n\t\t\t],\n\t\t\tsubmitOnClose: true\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\t_getHeaderButtons() {\n\t\tlet btns = super._getHeaderButtons();\n\t\tbtns[0].label = \"Save & Close\";\n\t\treturn btns;\n\t}\n\n\tgetSettingsData() {\n\t\tconst isDnD = game.system.id === 'dnd5e';\n\t\tlet data = {\n\t\t\t'modify-templates': game.settings.get('mess', 'modify-templates'),\n\t\t\t'change-placeables': game.settings.get('mess', 'change-placeables'),\n\t\t\t'templateTexture': game.settings.get('mess', 'templateTexture'),\n\t\t\t'templateAutoTargeting': game.settings.get('mess', 'templateAutoTargeting'),\n\t\t\t'templateDrawBordersOnlyOnHighlight': game.settings.get('mess', 'templateDrawBordersOnlyOnHighlight'),\n\t\t\t'better-draggable-lists': game.settings.get('mess', 'better-draggable-lists')\n\t\t};\n\t\tif (isDnD) {\n\t\t\tdata['templateTexture'] = game.settings.get('mess', 'templateTexture');\n\t\t\tdata['modify-rolling'] = game.settings.get('mess', 'modify-rolling');\n\t\t\tdata['max-critical'] = game.settings.get('mess', 'max-critical');\n\n\t\t\tdata['actor-item-sort'] = game.settings.get('mess', 'actor-item-sort');\n\t\t\t// data['better-draggable'] = game.settings.get('mess', 'better-draggable');\n\t\t\tdata['prepared-spell-tracker'] = game.settings.get('mess', 'prepared-spell-tracker');\n\t\t\tdata['add-scrolling'] = game.settings.get('mess', 'add-scrolling');\n\t\t}\n\t\treturn data;\n\t}\n\n\tgetData() {\n\t\tconst isDnD = game.system.id === 'dnd5e';\n\t\tlet data = super.getData();\n\t\tif (isDnD) {\n\t\t\tdata.dmgTypes = CONFIG.DND5E.damageTypes;\n\t\t\tdata.templateTypes = CONFIG.MeasuredTemplate.types;\n\t\t}\n\t\tdata.isDnD = isDnD;\n\t\tdata.settings = this.getSettingsData();\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\tconst data = expandObject(formData);\n\t\tfor (let [key, value] of Object.entries(data)) {\n\t\t\tgame.settings.set('mess', key, value);\n\t\t}\n\t\tnew Dialog({\n\t\t\tcontent: `

    ${game.i18n.localize(\"MESS.reloadReminder.text\")}

    `,\n\t\t\tbuttons: {\n\t\t\t\tyes: {\n\t\t\t\t\ticon: '',\n\t\t\t\t\tlabel: game.i18n.localize(\"MESS.reloadReminder.yes\"),\n\t\t\t\t\tcallback: () => location.reload()\n\t\t\t\t},\n\t\t\t\tno: {\n\t\t\t\t\ticon: '',\n\t\t\t\t\tlabel: game.i18n.localize(\"MESS.reloadReminder.no\")\n\t\t\t\t}\n\t\t\t}\n\t\t}).render(true);\n\t\t// game.settings.set('mess', 'templateTexture', mergeObject({}, formData))\n\t}\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/chat-popup/index.js","webpack:///./src/scripts/draggable-lists/draggable-list.js","webpack:///./src/scripts/draggable-lists/index.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/rolls/util.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","initChatPopUp","getElementById","contains","log","DraggableList","container","selector","mergeObject","defaultOptions","_init","offset","time","dir","onDragStart","onDragEnd","onDrop","_items","Array","from","rect","getBoundingClientRect","insideChild","clientY","height","clientX","width","_resetOffsets","idx","_initItem","position","_onDragEnterItem","_onDragLeaveItem","matches","headerRect","top","bottom","TweenLite","to","paddingTop","_over","item","timeEnd","initDraggableLists","oldFun","SidebarDirectory","activateListeners","list","querySelector","SceneDirectory","_onLazyLoadImage","entries","observer","isIntersecting","li","backgroundImage","children","img","unobserve","game","settings","CONFIG","debug","mess","sheet","render","user","isGM","moduleVersion","version","register","default","String","scope","oldVersion","isNewerVersion","init","changeTemplates","newFun","MeasuredTemplate","toString","replace","Function","getTargets","isTokenInside","ret","dndTemplateSettings","system","id","importedJS","import","AbilityTemplate","itemHook","_originalFromItem","fromItem","template","path","getFlag","hasDamage","damage","parts","loadTexture","then","tex","texture","activatePreviewListeners","token","scene","templatePos","startX","startY","currGrid","shape","tokens","getEmbeddedCollection","targets","updateTokenTargets","div","innerText","i18n","localize","formField","inp","dtype","insertAdjacentHTML","button","_activateFilePicker","after","addPreparedSpellTracker","isNPC","tracker","border","preparedSpells","sep","val","isNaN","setFlag","constructor","alignSelf","parentNode","insertBefore","getAllDmg","reduce","getAllVersatile","resultArray","splice","modifyApplyDmg","canApply","canApplyNonVersatile","canApplyVersatile","hasTarget","targetId","hasNoTarget","contextOptionsDmg","condition","icon","callback","applyDamage","content","floor","contextOptionsRoll","ContextMenu","MessContextMenu","find","setProperty","controlled","args","super","_menuItems","menuItems","filteredItems","filter","ol","frag","createDocumentFragment","join","close","_setPosition","_animateOpen","removeClass","sign","amount","card","sceneId","scenes","ui","notifications","Token","getEmbeddedEntity","owner","chatLogHook","advSelector","userId","templateData","advantage","normal","disadvantage","renderTemplate","arr","currIdx","findIndex","remove","newSelected","set","toggle","controls","childNodes","hit","dmg","getD20Modifier","getAdvantageSettings","rollD20","adv","nd","mods","halflingLucky","elvenAccuracy","diceFormula","reliableTalent","unshift","d20Mod","Roll","roll","d20","total","tooltip","getTooltip","crit","fumble","chatData","CONST","CHAT_MESSAGE_TYPES","OTHER","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","messageId","messages","isAuthor","Item","entityClass","_getChatCardActor","getOwnedItem","itemId","span","customTooltip","split","dataTerms","term","num","rgx","RegExp","Die","die","match","replaceChild","getDmgData","spellLevel","level","versatile","itm","bnsDmgParts","itmData","lbl","DND5E","damageTypes","scaling","newDmgPart","lvl","details","_scaleCantripDamage","_scaleSpellDamage","part","_evalParentheticalTerms","rollDmg","contextMenu","nextElementSibling","rolling","toggleItemBonusDamage","itemName","getSpeaker","actors","warn","newState","preCreateChatMessageHook","renderAttack","getFlavor","chatFlavor","rollTables","matchAll","collection","tables","tableData","table","entities","result","results","text","areaSkill","keys","areaTargetTypes","getProperty","size","attackData","toHit","dmgs","autoroll","allowed","_handleResourceConsumption","isCard","isAttack","targetActor","targetData","ac","label","attributes","traits","di","toUpperCase","substring","custom","dr","dv","attackTemplateData","flavor","autoRoll","whisper","getWhisperRecipients","toHitBtn","btns","actorSheetHook","abilityMods","off","abilityId","ability","abilities","mod","feats","remarkableAthlete","characterFlags","ceil","prof","jackOfAllTrades","checkBonus","format","saveMods","abl","saveBonus","skills","skillId","skill","skl","bonus","chatListeners","onChatCardAction","_onChatCardToggleContent","onMouseEnterTarget","onMouseLeaveTarget","onDblClickTarget","onToggleShowPlayers","action","placeTemplate","_onChatCardAction","getTargetToken","_onHoverIn","visible","_onHoverOut","pos","center","animatePan","tokenId","placeables","checked","oldGetTooltip","MessSettings","FormApplication","isDnD","hint","config","Boolean","registerMenu","restricted","loadTemplates","classes","tabs","navSelector","contentSelector","initial","submitOnClose","_getHeaderButtons","getData","dmgTypes","templateTypes","types","getSettingsData","formData","expandObject","Dialog","buttons","yes","location","reload","no"],"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}","export function initChatPopUp() {\n\tHooks.on('renderChatMessage', (app, html, options) => {\n\t\tif (document.getElementById('chat').classList.contains('active')) return;\n\n\n\t\tconsole.log('rendering chat message');\n\t\tconsole.log(app, html, options);\n\t\tconsole.log(document.getElementById('chat').classList.contains('active'))\n\t});\n\n\tconst hud = document.getElementById('hud');\n\n\tlet popupDiv = hud.appendChild(document.createElement('div'));\n\tpopupDiv.classList.add('mess-chat-popup')\n}","export class DraggableList {\n\tconstructor(container, selector, options = {}) {\n\t\tthis.container = container;\n\t\tthis.selector = selector;\n\t\tthis.options = mergeObject(this.defaultOptions, options);\n\n\t\tthis._init();\n\t}\n\t\n\tget defaultOptions() {\n\t\treturn {\n\t\t\toffset: 21, // in px\n\t\t\ttime: 0.2, // in seconds\n\t\t\tdir: null, // this selector will not get offset when hovered over\n\t\t\t// folowing are not used\n\t\t\tonDragStart: null,\n\t\t\tonDragEnd: null,\n\t\t\tonDrop: null,\n\t\t\t\n\t\t}\n\t}\n\n\tasync _init() {\n\t\t// this._items = Array.from(this.container.childNodes).filter(e => e.matches && e.matches(this.selector));\n\t\tthis._items = Array.from(this.container.querySelectorAll(this.selector));\n\t\tthis.container.addEventListener('dragleave', ev => {\n\t\t\tconst rect = this.container.getBoundingClientRect();\n\t\t\tconst boundary = 5;\n\t\t\t// Reset offsets, when first target was inside a child container.\n\t\t\tconst insideChild = ev.insideChild;\n\t\t\tif (!insideChild \n\t\t\t\t\t&& ev.clientY < rect.y + rect.height - boundary\n\t\t\t\t\t&& ev.clientY > rect.y + boundary\n\t\t\t\t\t&& ev.clientX > rect.x + boundary \n\t\t\t\t\t&& ev.clientX < rect.x - boundary + rect.width) {\n\t\t\t\t\t\tev.insideChild = true;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\tthis._resetOffsets();\n\t\t});\n\n /** Possibly modifying the drop target.\n\t * Why?\n\t * Due to the method used, the target gets moved out of the way and it will automatically drop onto the container element.\n\t * Most drop functions in fvtt just append to the end though, when dropping onto the container.\n\t * => We search for the first offset element and define that one as target. Since default for sorting is \"insertBefore\", and most implementations i've found build upon FVTT default/dnd5e systems default, this is a rather save implementation to find the real target.\n\t */\n\t\tthis.container.addEventListener('drop', ev => {\n\t\t\t// const insideChild = ev.insideChild;\n\t\t\t// if (this._over) {\n\t\t\t// \tObject.defineProperty(ev, 'target', {writable: false, value: this._over})\n\t\t\t// \t// Make sure that outer directories are not overwriting this stuff!\n\t\t\t// \t// ev.insideChild = true;\n\t\t\t// }\n\t\t});\n\t\tthis._items.forEach((e, idx) => this._initItem(e, idx))\n\t}\n\n\tasync _initItem(el, idx) {\n\t\tel.style.position = \"relative\";\n\t\tel.addEventListener('dragenter', ev => this._onDragEnterItem(ev, idx));\n\t\tel.addEventListener('dragleave', ev => this._onDragLeaveItem(ev, idx));\n\t\tel.addEventListener('dragend', ev => {\n\t\t\t// safety net if for some reasons the rerender is to slow or fails...\n\t\t\t// const srcElement = ev.currentTarget;\n\t\t\t// srcElement.style.opacity = null;\n\t\t\t// srcElement.style.height = null;\n\t\t\t// TweenLite.from(srcElement, this.options.time, {opacity:0, height:0});\n\t\t\tthis._resetOffsets();\n\t\t});\n\t\t// el.addEventListener('dragstart', ev => {\n\t\t// \tthis._dragged = ev.currentTarget;\n\t\t// \t// if (ev.currentTarget.matches(this.dir))\n\t\t// \t// \tthis._draggingDir = true;\n\t\t// \t// else \n\t\t// \t// \tthis._draggingDir = false;\n\t\t// \t// TweenLite.to(ev.currentTarget, this.options.time, {opacity: 0, height: 0});\n\t\t// })\n\t}\n\n\t_onDragEnterItem(ev, idx) {\n\t\tconsole.time('dragEnter')\n\t\tev.stopPropagation();\n\n\t\t// // save the current target\n\t\tconst time = this.options.time;\n\t\tconst offset = this.options.offset;\n\t\tthis._resetOffsets();\n\t\tlet target = ev.currentTarget;\n\t\tconsole.log(ev.currentTarget)\n\t\t// if inside a container, just make sure that its not jumping around. (Sometimes its ignoring the real target...)\n\t\tif (target.matches(this.options.dir)) {\n\t\t\tconst items = Array.from(target.querySelectorAll(this.selector));\n\t\t\tconst headerRect = target.getBoundingClientRect();\n\t\t\tif (ev.clientY > headerRect.top && ev.clientY < headerRect.bottom) {\n\t\t\t\tTweenLite.to(items, time, {paddingTop: 0});\n\t\t\t\tthis._over = target;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (let item of items) {\n\t\t\t\tconst rect = item.getBoundingClientRect();\n\t\t\t\tif (rect.bottom > ev.clientY) {\n\t\t\t\t\ttarget = item;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._over = target;\n\t\tconsole.log(target, offset);\n\t\tTweenLite.to(target, time, {paddingTop: offset});\n\t\t// let resetList = [];\n\t\t// let offsetList = [];\n\t\t// let prev = item.previousElementSibling;\n\t\t// while (prev) {\n\t\t// \t// TweenLite.to(prev, time, {top: 0})\n\t\t// \tresetList.push(prev);\n\t\t// \tprev = prev.previousElementSibling;\n\t\t// }\n\t\t// let next = item;\n\t\t// while (next) {\n\t\t// \tif (!this._draggingDir && next.matches(this.options.dir)) {\n\t\t// \t\t// TweenLite.to(next, time, {top: 0});\n\t\t// \t\tresetList.push(next);\n\t\t// \t} else\n\t\t// \t\toffsetList.push(next);\n\t\t// \t\t// TweenLite.to(next, time, {top: offset});\n\t\t// \tnext = next.nextElementSibling;\n\t\t// }\n\n\t\t// // // if (ev.currentTarget.matches(this.options.dir)) {\n\t\t\t\n\t\t// // // \tidx = idx + 1;\n\t\t// // // }\n\t\t// // // for (let i = idx; i < this._items.length; i++) {\n\t\t// // // \tconst rect = this._items[i].getBoundingClientRect();\n\t\t// // // }\n\t\t// // const resetList = this._items.slice(0, idx);\n\t\t// TweenLite.to(resetList, time, {top: 0});\n\t\t// // const offsetList = this._items.slice(idx);\n\t\t// TweenLite.to(offsetList, time, {top: offset});\n\n\t\tconsole.timeEnd('dragEnter')\n\t\treturn false;\n\t}\n\n\n\t_onDragLeaveItem(ev, idx) {\n\t\t// console.time('dragLeave')\n\t\tev.stopPropagation();\n\t\treturn false;\n\t\t// // Empty for now?\n\n\t\t// TweenLite.to(ev.currentTarget, 0.3, {transform: \"scale(1, 1)\"});\n\t\t// console.timeEnd('dragLeave')\n\t}\n\n\t// We now work with padding, cause.. reasons..\n\t_resetOffsets(time = this.options.time) {\n\t\t// TweenLite.to(this._items, time, {top: 0});\n\t\tTweenLite.to(this._items, time, {paddingTop: 0});\n\t}\n}\n","import {DraggableList} from './draggable-list';\n\nexport function initDraggableLists() {\n // make sure my listeners to get bound _before_ the class owns get bound to the elements\n // Only needed for the \"root\" directory though.. :/\n const oldFun = SidebarDirectory.prototype.activateListeners;\n SidebarDirectory.prototype.activateListeners = function(html) {\n // const list = html[0].querySelectorAll('.directory-list, .directory-item');\n // list.forEach(e => new DraggableList(e, '.entity'));\n const list = html[0].querySelector('.directory-list');\n new DraggableList(list, '.entity, .folder', {dir: '.folder'});\n oldFun.call(this, html);\n }\n\n // this does work, since on default most sheets have their drop function bound to the form, not the item list!\n Hooks.on('renderActorSheet', (app, html, options) => {\n const list = html[0].querySelectorAll('.item-list');\n list.forEach(e => new DraggableList(e, '.item'));\n });\n\n SceneDirectory.prototype._onLazyLoadImage = function(entries, observer) {\n for ( let e of entries ) {\n if ( !e.isIntersecting ) continue;\n const li = e.target;\n\n // Background Image\n if ( li.dataset.backgroundImage ) {\n li.children[0].style[\"background-image\"] = `url(\"${li.dataset.backgroundImage}\")`;\n delete li.dataset.backgroundImage;\n }\n\n // Avatar image\n const img = li.querySelector(\"img\");\n if ( img && img.dataset.src ) {\n img.src = img.dataset.src;\n delete img.dataset.src;\n }\n\n // No longer observe the target\n observer.unobserve(e.target);\n }\n }\n}","import { rolling } from './rolls';\nimport { MessSettings } from './settings.js';\nimport {dndTemplateSettings, changeTemplates } 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\nimport {initDraggableLists} from './draggable-lists';\nimport {initChatPopUp} from './chat-popup';\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\t// initChatPopUp();\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\tgame.mess = {};\n\tCONFIG.debug.mess = false;\n\tMessSettings.init();\n\n\trolling();\n\n\tdndTemplateSettings();\n\tchangeTemplates();\n\tif (game.settings.get('mess', 'change-placeables'))\n\t\tchangePlaceables();\n\tif (game.settings.get('mess', 'better-draggable-lists'))\n\t\tinitDraggableLists();\n});\n","export function changeTemplates() {\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 \n\tlet newFun = MeasuredTemplate.prototype.refresh.toString();\n\n\tif (game.settings.get('mess', 'modify-templates')) {\n\t\tnewFun = newFun.replace(/this\\.template\\.beginTextureFill\\(\\{[\\s\\S]*\\}\\)\\;/, `\n\t\t\t{\n\t\t\t\tlet mat = PIXI.Matrix.IDENTITY;\n\t\t\t\t// rectangle\n\t\t\t\tif (this.shape.width && this.shape.height)\n\t\t\t\t\tmat.scale(this.shape.width / this.texture.width, this.shape.height / this.texture.height);\n\t\t\t\telse if (this.shape.radius) {\n\t\t\t\t\tmat.scale(this.shape.radius * 2 / this.texture.height, this.shape.radius * 2 / this.texture.width)\n\t\t\t\t\t// Circle center is texture start...\n\t\t\t\t\tmat.translate(-this.shape.radius, -this.shape.radius);\n\t\t\t\t} else if (this.data.t === \"ray\") {\n\t\t\t\t\tconst d = canvas.dimensions,\n\t\t\t\t\t\t\t\theight = this.data.width * d.size / d.distance,\n\t\t\t\t\t\t\t\twidth = this.data.distance * d.size / d.distance;\n\t\t\t\t\tmat.scale(width / this.texture.width, height / this.texture.height);\n\t\t\t\t\tmat.translate(0, -height * 0.5);\n\n\t\t\t\t\tmat.rotate(toRadians(this.data.direction));\n\t\t\t\t} else {// cone\n\t\t\t\t\tconst d = canvas.dimensions;\n\t\t\t\n\t\t\t\t\t// Extract and prepare data\n\t\t\t\t\tlet {direction, distance, angle} = this.data;\n\t\t\t\t\tdistance *= (d.size / d.distance);\n\t\t\t\t\tdirection = toRadians(direction);\n\t\t\t\t\tconst width = this.data.distance * d.size / d.distance;\n\n\t\t\t\t\tconst angles = [(angle/-2), (angle/2)];\n\t\t\t\t\tdistance = distance / Math.cos(toRadians(angle/2));\n\t\t\t\n\t\t\t\t\t// Get the cone shape as a polygon\n\t\t\t\t\tconst rays = angles.map(a => Ray.fromAngle(0, 0, direction + toRadians(a), distance+1));\n\t\t\t\t\tconst height = Math.sqrt((rays[0].B.x - rays[1].B.x) * (rays[0].B.x - rays[1].B.x)\n\t\t\t\t\t\t\t\t\t\t\t\t\t+ (rays[0].B.y - rays[1].B.y) * (rays[0].B.y - rays[1].B.y));\n\t\t\t\t\tmat.scale(width / this.texture.width, height / this.texture.height);\n\t\t\t\t\tmat.translate(0, -height/2)\n\t\t\t\t\tmat.rotate(toRadians(this.data.direction));\n\t\t\t\t}\n\t\t\t\tthis.template.beginTextureFill({\n\t\t\t\t\ttexture: this.texture,\n\t\t\t\t\tmatrix: mat,\n\t\t\t\t\talpha: 0.8\n\t\t\t\t});\n\t\t\t\t// move into draw or so\n\t\t\t\tconst source = getProperty(this.texture, \"baseTexture.resource.source\")\n\t\t\t\tif ( source && (source.tagName === \"VIDEO\") ) {\n\t\t\t\t\tsource.loop = true;\n\t\t\t\t\tsource.muted = true;\n\t\t\t\t\tgame.video.play(source);\n\t\t\t\t}\n\t\t}`);\n\n\t\tHooks.on('renderMeasuredTemplateConfig', (app, html, data) => {\n\t\t\thtml[0].querySelector('.file-picker').dataset.type = 'imagevideo'\n\t\t});\n\t}\n\t\n\tif (game.settings.get('mess', 'templateAutoTargeting')) {\n\t\tnewFun = newFun.replace(/this\\.\\_borderThickness/, \"this.texture && !this._hover ? 0 : this._borderThickness\");\n\t\tnewFun = newFun.replace(/return\\sthis\\;/, `\n\t\t\tconst grid = canvas.grid;\n\t\t\t// only show grid highlights on hover\n\t\t\tif (this.texture) {\n\t\t\t\tconst hl = grid.getHighlightLayer(\\`Template.\\$\\{this.id\\}\\`);\n\t\t\t\tif (hl)\n\t\t\t\t\thl.renderable = this._hover;\n\t\t\t}\n\t\t\treturn this;`);\n\t}\n\n\tMeasuredTemplate.prototype.refresh = Function(`\"use strict\"; return ( function ${newFun} )`)();\n\n\t\n\tif (game.settings.get('mess', 'templateAutoTargeting')) {\n\t\tMeasuredTemplate.prototype.getTargets = getTargets;\n\t\tMeasuredTemplate.prototype.isTokenInside = isTokenInside;\n\n\t\tconst oldFun = MeasuredTemplate.prototype._onDragLeftMove;\n\t\tMeasuredTemplate.prototype._onDragLeftMove = function(ev) {\n\t\t\tconst ret = oldFun.bind(this)(ev);\n\n\t\t\tfor (let c of ev.data.clones)\n\t\t\t\tthis.getTargets(c);\n\n\t\t\treturn ret;\n\t\t}\n\t}\n}\n\nexport async function dndTemplateSettings() {\n if (game.system.id !== 'dnd5e') return;\n\n\tconst importedJS = (await import(/* webpackIgnore: true */ '/systems/dnd5e/module/pixi/ability-template.js'))\n\tconst AbilityTemplate = importedJS.default || importedJS.AbilityTemplate;\n\n\t// Auto texture creation from item\n\tif (game.settings.get('mess', 'modify-templates')) {\n\t\tHooks.on('renderItemSheet', itemHook);\n\t\t\n\t\t\n\t\tconst _originalFromItem = AbilityTemplate.fromItem;\n\t\tAbilityTemplate.fromItem = function(item) {\n\t\t\tconst template = _originalFromItem.bind(this)(item);\n\t\t\t\n\t\t\t// generate a texture based on the items dmg type, ...\n\t\t\t// Add settings to define custom templates for stuff.\n\t\t\tlet path = item.getFlag('mess', 'templateTexture');\n\t\t\tif (!path && item.hasDamage) {\n\t\t\t\tconst settings = game.settings.get('mess', 'templateTexture') || {};\n\t\t\t\tpath = settings[item.data.data.damage.parts[0][1]] || {};\n\t\t\t\tpath = path[template.data.t];\n\t\t\t}\n\t\t\tif (path)\n\t\t\t\tloadTexture(path).then(tex => {\n\t\t\t\t\ttemplate.texture = tex;\n\t\t\t\t\ttemplate.data.texture = path;\n\t\t\t\t\ttemplate.refresh();\n\t\t\t\t})\n\t\t\ttemplate.item = item;\n\t\t\treturn template;\n\t\t}\n\t}\n\n\n\tif (game.settings.get('mess', 'templateAutoTargeting')) {\n\t\t// rather ugly, maybe find a better way at some point :shrug:\n\t\tconst origPrevListeners = AbilityTemplate.prototype.activatePreviewListeners.toString();\n\t\tconst newFun = origPrevListeners.replace(/this\\.refresh\\(\\)\\;/, \n\t\t\t\t\t// get targets\n\t\t\t\t\t\t`this.refresh();\n\t\t\t\t\t\tthis.getTargets(this);\n\t\t\t\t\t`);\n\n\t\tAbilityTemplate.prototype.getTargets = getTargets;\n\t\tAbilityTemplate.prototype.isTokenInside = isTokenInside;\n\n\t\tAbilityTemplate.prototype.activatePreviewListeners = Function(`\"use strict\"; return ( function ${newFun} )`)();\n\t}\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(template) {\n\tconst tokens = canvas.scene.getEmbeddedCollection('Token');\n\tlet targets = [];\n\t\n\tfor (const token of tokens)\n\t\tif (template.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 = game.i18n.localize('MESS.itemSheet.templateTexture');\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\tconst target = html[0].querySelector('[name=\"data.target.units\"]');\n if (target)\n\t\ttarget.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\tif (actor.isNPC) return;\n\t\t\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-detail');\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 = `${game.i18n.localize('MESS.actorSheet.preparedSpellTracker')}: ${data.preparedSpells}`;\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\t\n\t\tif (app.constructor.name === 'Tidy5eSheet') {\n\t\t\tconst el = html[0].querySelector('.spellcasting-ability');\n\t\t\tif (!el)\n\t\t\t\treturn;\n\t\t\tel.appendChild(tracker, el);\n\t\t} else {\n\t\t\tconst el = html[0].querySelector('.spellbook .inventory-list');\n\t\t\tif (!el)\treturn;\n\t\t\ttracker.style.flex = '0';\n\t\t\ttracker.style.alignSelf = 'flex-start';\n\t\t\ttracker.style.margin = '0 8px';\n\t\t\tel.parentNode.insertBefore(tracker, el);\n\t\t}\n\t});\n}","function getAllDmg(li) {\n\tconst results = Array.from(li[0].parentNode.querySelectorAll('.mess-dice-result:not(.mess-versatile')).map(e => Number(e.querySelector('span').innerText));\n\treturn results.reduce((a, b) => a + b, 0);\n}\n\nfunction getAllVersatile(li) {\n\tconst resultArray = Array.from(li[0].parentNode.querySelectorAll('.mess-dice-result'));\n\tif (resultArray.length > 1 && resultArray[1].classList.contains('mess-versatile'))\n\t\tresultArray.splice(0,1)\n\tconst results = resultArray.map(e => Number(e.querySelector('span').innerText));\n\treturn results.reduce((a, b) => a + b, 0);\n}\n\nexport default function modifyApplyDmg() {\n\tconst canApply = (li) => true || canvas.tokens.controlled.length;\n\tconst canApplyNonVersatile = (li) => canApply(li) && li[0].parentNode.querySelector('.mess-dice-result:not(.mess-versatile)');\n\tconst canApplyVersatile = (li) => canApply(li) && li[0].parentNode.querySelector('.mess-versatile');\n\tconst hasTarget = (li) => !!li[0].closest('.mess-attack-card').dataset.targetId;\n\tconst hasNoTarget = (li) => !hasTarget(li);\n\tconst contextOptionsDmg = [\n\t\t{\n\t\t\tname: game.i18n.localize(\"MESS.attackCard.contextMenu.applyTarget\"),\n\t\t\tcondition: hasTarget\n\t\t},\n\t\t{\n\t\t\tname: game.i18n.localize(\"MESS.attackCard.contextMenu.applySelected\"),\n\t\t\tcondition: hasNoTarget\n\t\t},\n\t\t{\n\t\t\tname: game.i18n.localize(\"MESS.attackCard.contextMenu.dmg\"),\n\t\t\tcondition: canApplyNonVersatile,\n\t\t\ticon: ''\n\t\t},\n\t\t{\n\t\t\tname: 'full',\n\t\t\ticon: '',\n\t\t\tcondition: canApplyNonVersatile,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${getAllDmg(li)}`\n\t\t},\n\t\t{\n\t\t\tname: 'half',\n\t\t\ticon: '',\n\t\t\tcondition: canApplyNonVersatile,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${Math.max(Math.floor(getAllDmg(li) * 0.5), 1)}`\n\t\t},\n\t\t{\n\t\t\tname: 'double',\n\t\t\ticon: '',\n\t\t\tcondition: canApplyNonVersatile,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${Math.floor(getAllDmg(li) * 2)}`\n\t\t},\n\t\t{\n\t\t\tname: game.i18n.localize(\"MESS.attackCard.contextMenu.healing\"),\n\t\t\tcondition: canApplyNonVersatile,\n\t\t\ticon: ''\n\t\t},\n\t\t{\n\t\t\tname: 'full',\n\t\t\ticon: '',\n\t\t\tcondition: canApplyNonVersatile,\n\t\t\tcallback: (target, li) => applyDamage(li, -1),\n\t\t\tcontent: (li) => `${getAllDmg(li)}`\n\t\t},\n\t\t{\n\t\t\tname: [game.i18n.localize('DND5E.Versatile'), ' - ', game.i18n.localize(\"MESS.attackCard.contextMenu.dmg\")],\n\t\t\tcondition: canApplyVersatile,\n\t\t\ticon: ''\n\t\t},\n\t\t{\n\t\t\tname: 'full',\n\t\t\ticon: '',\n\t\t\tcondition: canApplyVersatile,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${getAllVersatile(li)}`\n\t\t},\n\t\t{\n\t\t\tname: 'half',\n\t\t\ticon: '',\n\t\t\tcondition: canApplyVersatile,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${Math.max(Math.floor(getAllVersatile(li) * 0.5), 1)}`\n\t\t},\n\t\t{\n\t\t\tname: 'double',\n\t\t\ticon: '',\n\t\t\tcondition: canApplyVersatile,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${Math.floor(getAllVersatile(li) * 2)}`\n\t\t},\n\t\t{\n\t\t\tname: [game.i18n.localize('DND5E.Versatile'), ' - ', game.i18n.localize(\"MESS.attackCard.contextMenu.healing\")],\n\t\t\tcondition: canApplyVersatile,\n\t\t\ticon: ''\n\t\t},\n\t\t{\n\t\t\tname: 'full',\n\t\t\ticon: '',\n\t\t\tcondition: canApplyVersatile,\n\t\t\tcallback: (target, li) => applyDamage(li, -1),\n\t\t\tcontent: (li) => `${getAllVersatile(li)}`\n\t\t}\n\t]\n\n\n\tconst contextOptionsRoll = [\n\t\t{\n\t\t\tname: game.i18n.localize(\"MESS.attackCard.contextMenu.applyTarget\"),\n\t\t\tcondition: hasTarget\n\t\t},\n\t\t{\n\t\t\tname: game.i18n.localize(\"MESS.attackCard.contextMenu.applySelected\"),\n\t\t\tcondition: hasNoTarget\n\t\t},\n\t\t{\n\t\t\tname: game.i18n.localize(\"MESS.attackCard.contextMenu.dmg\"),\n\t\t\tcondition: canApply,\n\t\t\ticon: ''\n\t\t},\n\t\t{\n\t\t\tname: 'full',\n\t\t\ticon: '',\n\t\t\tcondition: canApply,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${li[0].querySelector('span').innerText}`\n\t\t},\n\t\t{\n\t\t\tname: 'half',\n\t\t\ticon: '',\n\t\t\tcondition: canApply,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${Math.max(Math.floor(Number(li[0].querySelector('span').innerText) * 0.5), 1)}`\n\t\t},\n\t\t{\n\t\t\tname: 'double',\n\t\t\ticon: '',\n\t\t\tcondition: canApply,\n\t\t\tcallback: (target, li) => applyDamage(li, 1),\n\t\t\tcontent: (li) => `${Math.floor(Number(li[0].querySelector('span').innerText) * 2)}`\n\t\t},\n\t\t{\n\t\t\tname: game.i18n.localize(\"MESS.attackCard.contextMenu.healing\"),\n\t\t\tcondition: canApply,\n\t\t\ticon: ''\n\t\t},\n\t\t{\n\t\t\tname: 'full',\n\t\t\ticon: '',\n\t\t\tcondition: canApply,\n\t\t\tcallback: (target, li) => applyDamage(li, -1),\n\t\t\tcontent: (li) => `${li[0].querySelector('span').innerText}`\n\t\t}\n\t]\n\n\n\tContextMenu.prototype.bind = Function('\"use strict\"; return ( function ' + ContextMenu.prototype.bind.toString().replace(/this\\.render\\(parent\\)/,`this.render(parent, event)`) + ')')();\n\n\tHooks.on('renderChatLog', (app, html, options) => {\n\t\tnew MessContextMenu(html.find('#chat-log'), \".mess-chat-dmg .mess-dice-result\", contextOptionsRoll);\n\t\tnew MessContextMenu(html.find('#chat-log'), \".mess-chat-dmg .mess-chat-roll-type\", contextOptionsDmg);\n\t})\n\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}\n\nclass MessContextMenu extends ContextMenu {\n\tconstructor(...args) {\n\t\tsuper(...args);\n\n\t\tthis._menuItems = this.menuItems;\n\t}\n\n\trender(target, event) {\n\t\tconst filteredItems = this._menuItems.filter(item => {\n\t\t\tif (!item.condition) return true;\n\t\t\tif (!(item.condition instanceof Function)) return item.condition;\n\t\t\treturn item.condition(target);\n\t\t})\n\n\t\tif (filteredItems.length) event.stopPropagation();\n\t\telse return;\n\t\tthis.menuItems = filteredItems.map(e => {\n\t\t\tif (e.content)\n\t\t\t\te.name = e.content(target);\n\t\t\treturn e;\n\t\t});\n\n let html = $(\"#context-menu\").length ? $(\"#context-menu\") : $('');\n let ol = $('
      ');\n html.html(ol);\n\n\t\tconst frag = document.createDocumentFragment();\n\t\tfor (let item of this.menuItems) {\n\t\t\tconst li = document.createElement('li')\n\t\t\t\n\t\t\tif (item.name instanceof Array)\n\t\t\t\titem.name = item.name.map(e => game.i18n.localize(e)).join(\"\");\n\n\t\t\tif (item.icon)\n\t\t\t\tli.innerHTML = `${item.icon}${game.i18n.localize(item.name)}`;\n\t\t\telse\n\t\t\t\tli.innerHTML = game.i18n.localize(item.name);\n\n\t\t\tif (item.callback) {\n\t\t\t\tli.addEventListener('click', ev => {\n\t\t\t\t\tev.preventDefault();\n\t\t\t\t\tev.stopPropagation();\n\t\t\t\t\titem.callback(target, li);\n\t\t\t\t\tthis.close();\n\t\t\t\t});\n\t\t\t\tli.classList.add('context-item');\n\t\t\t} else\n\t\t\t\tli.classList.add('mess-context-menu-header');\n\n\t\t\tfrag.appendChild(li);\n\t\t}\n\n\t\tol[0].appendChild(frag);\n\n // Append to target\n this._setPosition(html, target);\n\n // Animate open the menu\n return this._animateOpen(html);\n\t}\n\n\t_setPosition(html, target) {\n\t\thtml.removeClass(\"expand-up expand-down\");\n\t\tsuper._setPosition(html, target);\n\t}\n}\n\nasync function applyDamage(target, sign) {\n\tconst amount = Number(target.querySelector('span').innerText);\n\tconst card = target.closest('.mess-attack-card')\n\tconst targetId = card.dataset.targetId\n\tif (targetId) {\n\t\tconst sceneId = card.dataset.sceneId;\n\t\tconst scene = game.scenes.get(sceneId);\n\t\tif (!scene) {\n\t\t\tui.notifications.error(game.i18n.localize(\"MESS.attackCard.applyDamage.sceneNotFound\"));\n\t\t\treturn;\n\t\t}\n\t\tconst token = new Token(scene.getEmbeddedEntity(\"Token\", targetId));\n\t\tif (!token) {\n\t\t\tui.notifications.error(game.i18n.localize(\"MESS.attackCard.applyDamage.targetNotFound\"));\n\t\t\treturn;\n\t\t}\n\t\tif (!token.owner) {\n\t\t\tui.notifications.error(game.i18n.localize(\"MESS.attackCard.applyDamage.targetNotOwner\"));\n\t\t\treturn;\n\t\t}\n\t\tconst actor = token.actor;\n\t\tactor.applyDamage(amount, sign);\n\t} else {\n\t\tconst tokens = canvas.tokens.controlled;\n\t\tif (!tokens.length) {\n\t\t\tui.notifications.error(game.i18n.localize('MESS.attackCard.contextMenu.error'));\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tfor (const token of tokens) {\n\t\t\tconst a = token.actor;\n\t\t\ta.applyDamage(amount, sign);\n\t\t}\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\thtml[0].classList.add('mess');\n\tconsole.log(\"qwe\", game.user, game.user.isGM)\n\tif (game.user.isGM)\n\t\thtml[0].classList.add('mess-is-gm');\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}","import {getTargetToken, hasTarget} from './util.js';\n\n/**\n * All the functions provided here are heavily based on Foundrys DnD5e system, authored by Atropos.\n * Original repository: https://gitlab.com/foundrynet/dnd5e\n * Original Author: Atropos\n * License: GNU GPLv3\n */\n\nfunction 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\t\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 false;\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\t// div.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\tif (game.user.isGM)\n\t\tdiv.classList.add('mess-gm-dice');\n\tconst span = div.appendChild(document.createElement('span'));\n\tspan.classList.add('mess-roll-container');\n\tspan.innerHTML = `${r.total}`;\n\n\tdiv.insertAdjacentHTML('beforeend', await r.getTooltip());\n\tlet customTooltip = '';\n\tconst terms = rollData.terms.split('+'); // only adding + terms here anyway\n\tconst dataTerms = rollData.formula.split(/(?=[+-])/);\n\tfor (let i = 0; i < terms.length; i++) {\n\t\tconst term = terms[i];\n\t\tconst num = Number(dataTerms[i]);\n\t\tif (isNaN(num)) continue;\n\t\tcustomTooltip += `
      \n\t\t\t
      \n\t\t\t\t

      \n\t\t\t\t\t${term}\n\t\t\t\t\t${num >= 0 ? '+'+num : num}\n\t\t\t\t

      \n\t\t\t
      \n\t\t
      `\n\t}\n\tdiv.querySelector('.dice-tooltip').insertAdjacentHTML('beforeend', customTooltip);\n\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\n\t// if (hasTarget(button)) {\n\t// \t// Check if hit\n\t// \tconst target = getTargetToken(button);\n\t// \tconst ac = target.actor.data.data.attributes.ac.value;\n\t// \tif (ac) {\n\t// \t\tif ((r.total >= ac && d20 > fumble) || d20 >= crit) {\n\t// \t\t\tspan.classList.add('mess-hit');\n\t// \t\t} else {\n\t// \t\t\tspan.classList.add('mess-miss');\n\t// \t\t}\n\t// \t}\n\t// }\n\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 rgx = new RegExp(Die.rgx.die, \"g\");\n\t\t\tconst formula = e.dataset.formula.replace(rgx, (match, nd, d, mods) => {\n\t\t\t\tif (game.settings.get('mess', 'max-critical'))\n\t\t\t\t\t\tmods = \" + \" + nd * d + (mods || \"\");\n\t\t\t\telse {\n\t\t\t\t\t\tnd = nd * 2;\n\t\t\t\t\t\tmods = mods || \"\";\n\t\t\t\t}\n\t\t\t\treturn nd + \"d\" + d + mods;\n\t\t\t});\n\t\t\te.innerHTML = ` ${formula}`;\n\t\t\te.dataset.formula = 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\n\t// Only apply for items that are not bonus dmg themself\n\tif (!item.getFlag('mess', 'isBonusDamage'))\n\t\tfor (let itm of actor.items){\n\t\t\t// skip self\n\t\t\tif (itm.id === item.id) continue;\n\n\t\t\tlet bnsDmgParts = [];\n\t\t\tif (itm.getFlag('mess', 'isBonusDamage')){\n\t\t\t\tif (itm.hasDamage){\n\t\t\t\t\tconst itmData = itm.data.data;\n\t\t\t\t\tbnsDmgParts.push(itmData.damage.parts[0][0]);\n\t\t\t\t\tvar lbl = itm.name;\n\t\t\t\t\tif (itmData.damage.parts[0][1].length > 0) lbl += ` - ${game.i18n.localize('DND5E.Damage' + CONFIG.DND5E.damageTypes[itmData.damage.parts[0][1]])}`;\n\t\t\t\t\tbnsDmgParts.push(lbl);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bnsDmgParts.length > 0) rollData.parts.push(bnsDmgParts);\n\t\t}\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\trollData.parts[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\n\t\t//evalute damage formula's for example \"ceil(@classes.rogue.levels/2))d6\" -> \"4d6\"\n\t\tlet terms = roll._evalParentheticalTerms(roll.formula).map(t => {\n\t\t\tif ( t instanceof Roll ) {\n\t\t\t\tt.roll();\n\t\t\t\treturn t.total;\n\t\t\t}\n\t\t\treturn t;\n\t\t});\n\t\tpart.push(terms.join(''));\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\tconst contextMenu = ev.currentTarget.parentNode.querySelector('#context-menu');\n\tif (contextMenu)\n\t\tcontextMenu.remove();\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 false;\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\t// div.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');\t\n\tif (game.user.isGM)\n\t\tdiv.classList.add('mess-gm-dice');\n\n\t// small helper to detect if versatile dmg was rolled\n\tconst dmgType = button.nextElementSibling.innerText;\n\tif (dmgType === game.i18n.localize('DND5E.Versatile'))\n\t\tdiv.classList.add('mess-versatile');\n\n\tconst span = div.appendChild(document.createElement('span'));\n\tspan.classList.add('mess-roll-container');\n\tspan.innerHTML = `${r.total}`;\n\tdiv.insertAdjacentHTML('beforeend', await r.getTooltip());\n\tlet customTooltip = '';\n\tconst terms = button.dataset.terms.split(/(?=[+-])/);\n\tconst dataTerms = formula.split(/(?=[+-])/).filter(e => e !== '+' && e !== '-'); // filter single + and -\n\t// i really sh ould put this into a function............\n\tfor (let i = 0; i < terms.length; i++) {\n\t\tconst term = terms[i];\n\t\tconst num = Number(dataTerms[i].replace(/\\s/g, ''));\n\t\tconsole.log(dataTerms[i], num)\n\t\tif (isNaN(num)) continue;\n\t\tcustomTooltip += `
      \n\t\t\t
      \n\t\t\t\t

      \n\t\t\t\t\t${term}\n\t\t\t\t\t${num >= 0 ? '+'+num : num}\n\t\t\t\t

      \n\t\t\t
      \n\t\t
      `\n\t}\n\tdiv.querySelector('.dice-tooltip').insertAdjacentHTML('beforeend', customTooltip);\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\tgame.mess.toggleItemBonusDamage = toggleItemBonusDamage;\n\n\tconst oldGetTooltip = Roll.prototype.getTooltip;\n\tRoll.prototype.getTooltip = function() {\n\t\tfor (let i = 0; i < this.parts.length; )\n\t\treturn oldGetTooltip.call(this);\n\t}\n}\n\n/**\n * Heavily based on: https://gitlab.com/foundrynet/dnd5e/-/blob/master/module/macros.js#L42\n * original author: Atropos\n * source repository: https://gitlab.com/foundrynet/dnd5e\n * license: GPLv3\n * @param {*} itemName \n */\nfunction toggleItemBonusDamage(itemName) {\n\tconst speaker = ChatMessage.getSpeaker();\n let actor;\n\tif ( speaker.token ) \n\t\tactor = game.actors.tokens[speaker.token];\n\tif ( !actor ) \n\t\tactor = game.actors.get(speaker.actor);\n // Get matching items\n const items = actor ? actor.items.filter(i => i.name === itemName) : [];\n if ( items.length > 1 ) {\n ui.notifications.warn(`Your controlled Actor ${actor.name} has more than one Item with name ${itemName}. The first matched item will be chosen.`);\n } else if ( items.length === 0 ) {\n return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`);\n }\n const item = items[0];\n\n\tconst newState = !item.getFlag('mess', 'isBonusDamage');\n\t// toggle bonus dmg\n\titem.setFlag('mess', 'isBonusDamage', newState);\n\treturn newState;\n}\n\n/**\n * Initializes all the hoohks!\n */\nfunction setupHooks() {\n\tCONFIG.Item.entityClass.chatListeners = chatListeners.bind(CONFIG.Item.entityClass);\n\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\t// Hooks.on('ready', chatListeners.bind(CONFIG.Item.entityClass));\n\n\tHooks.on('renderItemSheet', (app, html, data) => {\n\t\tlet div = document.createElement('div');\n\t\tdiv.classList.add('form-group');\n\t\tdiv.appendChild(document.createElement('label')).innerText = game.i18n.localize('MESS.itemSheet.bonusDmg');\n\t\tlet formField = div.appendChild(document.createElement('div'));\n\t\tformField.classList.add('form-fields');\n\t\tlet inp = formField.appendChild(document.createElement('input'));\n\t\tinp.type = 'checkbox';\n\t\tinp.name = 'flags.mess.isBonusDamage';\n\t\tinp.checked = app.object.getFlag('mess', 'isBonusDamage');\n\n\t\tconst target = html[0].querySelector('[name=\"data.formula\"]');\n\t\tif (target)\n\t\t\ttarget.closest('.form-group').after(div);\n\t})\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// TODO: for compendium rolltables\nfunction getFlavor(chatFlavor, target) {\n\tconst rollTableRegExp = /@RollTable\\[([^\\]])+\\](?:\\{([^\\}]+)\\})?/g;\n\tlet rollTables = Array.from(chatFlavor.matchAll(rollTableRegExp));\n\tif (rollTables) {\n\t\tconst collection = game.tables;\n\t\tfor (let tableData of rollTables) {\n\t\t\tlet table;\n\t\t\tconst id = tableData[0].match(/\\[[a-zA-Z0-9]{16}\\]/);\n\t\t\tif (id) {\n\t\t\t\ttable = collection.get(id[0].slice(1, -1));\n\t\t\t} else {\n\t\t\t\tconst name = tableData[0].match(/\\[([^\\]])+\\]/)[0].slice(1, -1);\n\t\t\t\ttable = collection.entities.find(e => e.data.name === name);\n\t\t\t}\n\t\t\tlet result = table.roll();\n\t\t\tchatFlavor = chatFlavor.replace(tableData[0], result.results.map(e => e.text).join(\",\"));\n\t\t}\n\t}\n\treturn chatFlavor.replace(/\\[target\\.name\\]/g, target.data.name)\n}\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\tisGM: game.user.isGM\n\t}\n\n\tconst autoroll = game.settings.get('mess', `${game.userId}.autoroll-selector`);\n\n\tfor (const target of targets) {\n\t\tconst allowed = await item._handleResourceConsumption({isCard: false, isAttack: true});\n\t\tconst targetActor = target.actor ? target.actor.data : null;\n\t\tlet targetData = {};\n\t\tif (targetActor) {\n\t\t\ttargetData.ac = {\n\t\t\t\tlabel: '',\n\t\t\t\ttitle: game.i18n.localize('DND5E.ArmorClass'),\n\t\t\t\tvalue: targetActor.data.attributes.ac.value\n\t\t\t}\n\t\t\tconsole.log(targetActor.data.traits)\n\t\t\tlet di = targetActor.data.traits.di.value.filter(e => e !== \"custom\").map(e => game.i18n.localize('DND5E.Damage' + e[0].toUpperCase() + e.substring(1)));\n\t\t\tif (targetActor.data.traits.di.custom)\n\t\t\t\tdi.push(targetActor.data.traits.di.custom);\n\t\t\ttargetData.di = {\n\t\t\t\tlabel: '',\n\t\t\t\ttitle: game.i18n.localize('DND5E.DamImm'),\n\t\t\t\tvalue: di.join(\", \")\n\t\t\t}\n\n\t\t\tlet dr = targetActor.data.traits.dr.value.filter(e => e !== \"custom\").map(e => game.i18n.localize('DND5E.Damage' + e[0].toUpperCase() + e.substring(1)));\n\t\t\tconsole.log(dr);\n\t\t\tif (targetActor.data.traits.dr.custom) \n\t\t\t\tdr.push(targetActor.data.traits.dr.custom);\n\t\t\ttargetData.dr = {\n\t\t\t\tlabel: '',\n\t\t\t\ttitle: game.i18n.localize('DND5E.DamRes'),\n\t\t\t\tvalue: dr.join(\", \")\n\t\t\t}\n\t\t\tlet dv = targetActor.data.traits.dv.value.filter(e => e !== \"custom\").map(e => game.i18n.localize('DND5E.Damage' + e[0].toUpperCase() + e.substring(1)));\n\t\t\tif (targetActor.data.traits.dv.custom)\n\t\t\t\tdv.push(targetActor.data.traits.dv.custom);\n\t\t\ttargetData.dv = {\n\t\t\t\tlabel: '',\n\t\t\t\ttitle: game.i18n.localize('DND5E.DamVuln'),\n\t\t\t\tvalue: dv.join(\", \")\n\t\t\t}\n\t\t\t// console.log(targetData)\n\t\t}\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\ttargetData: targetData,\n\t\t\t\t\t\t\t\t\tflavor: getFlavor(item.data.data.chatFlavor, target),\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 (!game.user.isGM || !game.settings.get('mess', 'attack-card-always-public')) {\n\t\t\tconst rollMode = game.settings.get(\"core\", \"rollMode\");\n\t\t\tif ( [\"gmroll\", \"blindroll\"].includes(rollMode) ) chatData.whisper = ChatMessage.getWhisperRecipients(\"GM\");\n\t\t\t// doesn't work anyway since its no \"real\" roll....\n // if ( rollMode === \"blindroll\" ) chatData.blind = true;\n if ( rollMode === \"selfroll\" ) chatData.whisper = [game.user.id];\n\t\t}\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\t\t// Add feat-related proficiency bonuses\n\t\tconst actorData = getProperty(app.object, \"data.data\")\n if ( feats.remarkableAthlete && DND5E.characterFlags.remarkableAthlete.abilities.includes(abilityId) ) {\n parts.push(\"@remarkable-athlete\");\n data[\"remarkable-athlete\"] = Math.ceil(0.5 * actorData.attributes.prof);\n }\n else if ( feats.jackOfAllTrades ) {\n parts.push(\"@jack-of-all-trades\");\n data[\"jack-of-all-trades\"] = Math.floor(0.5 * actorData.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 false;\n\t}));\n\tconst saveMods = html[0].querySelectorAll('.ability-save');\n\t$(saveMods).off();\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\treturn false;\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\t\tdata.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(html) {\n\tif (!html)\n\t\thtml = $(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\thtml.on('click', '.mess-show-btn', onToggleShowPlayers);\n}\n\n// Only overwrite stuff for attack buttons\nasync function onChatCardAction (ev) {\n\tev.preventDefault(); ev.stopPropagation();\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}\n\nasync function onToggleShowPlayers(ev) {\n\tconst div = ev.currentTarget.closest('.mess-chat-to-hit, .mess-chat-dmg');\n\tdiv.classList.toggle('mess-show-players');\n\n\tconst card = ev.currentTarget.closest('.mess-attack-card');\n\tconst messageId = card.closest(\".message\").dataset.messageId;\n\tconst message = game.messages.get(messageId);\n\tmessage.update({content: card.parentNode.innerHTML});\n}","export function getTargetToken(el) {\n\t\n\tconst card = el.closest('.mess-attack-card')\n\n\tconst targetId = card.dataset.targetId\n\tconst sceneId = card.dataset.sceneId;\n\tconst scene = game.scenes.get(sceneId);\n\tconst token = new Token(scene.getEmbeddedEntity(\"Token\", targetId));\n\treturn token;\n}\n\nexport function hasTarget(el) {\n\tconst card = el.closest('.mess-attack-card');\n\tif (!card) return false;\n\tif (card.dataset.targetId) return true;\n\treturn false;\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: \"world\",\n\t\t\tconfig: false,\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: \"world\",\n\t\t\tconfig: false,// Change if implemented\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t})\n\n\t\tgame.settings.register('mess', 'better-draggable-lists', {\n\t\t\tname: \"Activate better drag'n'drop workflow for lists.\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: false,\n\t\t\tdefault: true,\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: \"world\",\n\t\t\tconfig: false,\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: \"world\",\n\t\t\tconfig: false,\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: false,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t});\n\n\t\tgame.settings.register('mess', 'attack-card-always-public', {\n\t\t\tname: \"Roll mode for alternative rolling.\",\n\t\t\thint: \"Always roll attack rolls public, with hidden rolls, but visible target.\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: false,\n\t\t\tdefault: isDnD,\n\t\t\ttype: Boolean\n\t\t});\n\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: false,\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: false,\n\t\t\tdefault: true,\n\t\t\ttype: Boolean\n\t\t});\t\n\t\tgame.settings.registerMenu('mess', 'templateTexture', {\n\t\t\tname: game.i18n.localize('MESS.FVTTSettings.description'),\n\t\t\tlabel: game.i18n.localize('MESS.FVTTSettings.button'),\n\t\t\ticon: \"fas fa-mug-hot\",\n\t\t\ttype: MessSettings,\n\t\t\trestricted: true\n\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\n\t\tgame.settings.register('mess', 'max-critical', {\n\t\t\tname: \"Activate maximum critical rolls.\",\n\t\t\thint: \"Changes behaviour of critical damage rolls to maximize the damage of the extra dice for criticals!\",\n\t\t\tscope: \"world\",\n\t\t\tconfig: false,\n\t\t\tdefault: false,\n\t\t\ttype: Boolean\n\t\t});\n\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\tconfig: false,\n\t\t\tdefault: true,\n\t\t\ttype: Object\n\t\t});\t\n\n\t\tgame.settings.register('mess', 'templateAutoTargeting', {\n\t\t\tscope: \"world\",\n\t\t\tdefault: true,\n\t\t\ttype: Boolean\n\t\t})\n\n\t\tgame.settings.register('mess', 'templateDrawBordersOnlyOnHighlight', {\n\t\t\tscope: \"world\",\n\t\t\tdefault: true,\n\t\t\ttype: Boolean\n\t\t})\n\n\t\tthis.loadTemplates();\n\t}\n\tstatic loadTemplates() {\n\t\tloadTemplates([\n\t\t\t'modules/mess/templates/settings/templates.html',\n\t\t\t'modules/mess/templates/settings/dnd5e.html',\n\t\t\t'modules/mess/templates/settings/misc.html'\n\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/settings.html\",\n\t\t\theight: \"auto\",\n\t\t\ttitle: \"Mess - Moerills enhancing super-suit(e) - Settings Menu\",\n\t\t\twidth: 600,\n\t\t\tclasses: [\"mess\", \"settings\"],\n\t\t\ttabs: [ \n\t\t\t\t{\n\t\t\t\t\tnavSelector: '.tabs',\n\t\t\t\t\tcontentSelector: 'form',\n\t\t\t\t\tinitial: 'name'\n\t\t\t\t} \n\t\t\t],\n\t\t\tsubmitOnClose: true\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\t_getHeaderButtons() {\n\t\tlet btns = super._getHeaderButtons();\n\t\tbtns[0].label = \"Save & Close\";\n\t\treturn btns;\n\t}\n\n\tgetSettingsData() {\n\t\tconst isDnD = game.system.id === 'dnd5e';\n\t\tlet data = {\n\t\t\t'modify-templates': game.settings.get('mess', 'modify-templates'),\n\t\t\t'change-placeables': game.settings.get('mess', 'change-placeables'),\n\t\t\t'templateTexture': game.settings.get('mess', 'templateTexture'),\n\t\t\t'templateAutoTargeting': game.settings.get('mess', 'templateAutoTargeting'),\n\t\t\t'templateDrawBordersOnlyOnHighlight': game.settings.get('mess', 'templateDrawBordersOnlyOnHighlight'),\n\t\t\t'better-draggable-lists': game.settings.get('mess', 'better-draggable-lists')\n\t\t};\n\t\tif (isDnD) {\n\t\t\tdata['templateTexture'] = game.settings.get('mess', 'templateTexture');\n\t\t\tdata['modify-rolling'] = game.settings.get('mess', 'modify-rolling');\n\t\t\tdata['max-critical'] = game.settings.get('mess', 'max-critical');\n\n\t\t\tdata['actor-item-sort'] = game.settings.get('mess', 'actor-item-sort');\n\t\t\t// data['better-draggable'] = game.settings.get('mess', 'better-draggable');\n\t\t\tdata['prepared-spell-tracker'] = game.settings.get('mess', 'prepared-spell-tracker');\n\t\t\tdata['add-scrolling'] = game.settings.get('mess', 'add-scrolling');\n\t\t\tdata['attack-card-always-public'] = game.settings.get('mess', 'attack-card-always-public');\n\t\t}\n\t\treturn data;\n\t}\n\n\tgetData() {\n\t\tconst isDnD = game.system.id === 'dnd5e';\n\t\tlet data = super.getData();\n\t\tif (isDnD) {\n\t\t\tdata.dmgTypes = CONFIG.DND5E.damageTypes;\n\t\t\tdata.templateTypes = CONFIG.MeasuredTemplate.types;\n\t\t}\n\t\tdata.isDnD = isDnD;\n\t\tdata.settings = this.getSettingsData();\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\tconst data = expandObject(formData);\n\t\tfor (let [key, value] of Object.entries(data)) {\n\t\t\tgame.settings.set('mess', key, value);\n\t\t}\n\t\tnew Dialog({\n\t\t\tcontent: `

      ${game.i18n.localize(\"MESS.reloadReminder.text\")}

      `,\n\t\t\tbuttons: {\n\t\t\t\tyes: {\n\t\t\t\t\ticon: '',\n\t\t\t\t\tlabel: game.i18n.localize(\"MESS.reloadReminder.yes\"),\n\t\t\t\t\tcallback: () => location.reload()\n\t\t\t\t},\n\t\t\t\tno: {\n\t\t\t\t\ticon: '',\n\t\t\t\t\tlabel: game.i18n.localize(\"MESS.reloadReminder.no\")\n\t\t\t\t}\n\t\t\t}\n\t\t}).render(true);\n\t\t// game.settings.set('mess', 'templateTexture', mergeObject({}, formData))\n\t}\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/templates/attack-card.html b/dist/templates/attack-card.html index 1fc722a..b32e1b2 100644 --- a/dist/templates/attack-card.html +++ b/dist/templates/attack-card.html @@ -7,20 +7,31 @@ {{/unless}} {{#if target.img}}
      - {{localize 'MESS.attackCard.target'}}:
      {{target.name}}
      +
      +
      + {{!--localize 'MESS.attackCard.target'}}:
      --}}{{target.name}}
      +
        + {{#each targetData}} + {{#if this.value}} +
      • {{this.value}}
      • + {{/if}} + {{/each}} +
      +
      +
      {{/if}} -
      +
      {{#if toHit}}
      - - + +
      {{/if}} {{#if dmgs}}
      - + {{#each dmgs.parts}} diff --git a/dist/templates/settings/dnd5e.html b/dist/templates/settings/dnd5e.html index 78ee61f..6a960a7 100644 --- a/dist/templates/settings/dnd5e.html +++ b/dist/templates/settings/dnd5e.html @@ -19,6 +19,10 @@

      {{localize 'MESS.settings.dnd5e.rolling.header'}} {{localize 'MESS.settings.dnd5e.rolling.hint'}}

      +
      + + {{{localize 'MESS.settings.dnd5e.rolling.mode.hint'}}} +
      {{!-- does not exist atm --}} diff --git a/dist/templates/welcome-screen.html b/dist/templates/welcome-screen.html index 83b2246..7b9b564 100644 --- a/dist/templates/welcome-screen.html +++ b/dist/templates/welcome-screen.html @@ -8,6 +8,8 @@

      Thank you for using this Mess!

      +

      Please keep in mind that i am working on this and my other projects mostly alone, in my spare time and for free. Especially before complaining (rudely) about something not working or needing some time before getting done!

      +

      Stay polite and do your best to describe your issue, question or suggestion as detailed as possible, to help shorten time needed to clarify things and to even keep me motivated to react and go on working on this for free.

      Make sure to read the README, for important informations and 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. @@ -22,6 +24,23 @@

      Want to support development? Click here

      Changelog

      +

      v0.10.0

      +
        +
      • Redid the drag animations for a hopefully better experience. Please try this again if you previously had issues. If smth still doesn't work, please provide feedback on github.
      • +
      • *NEW* Portuguese localization thanks to GitHub user @rinnocenti.
      • +
      • *NEW* GM only information on attack cards. Added some useful information of the target to chat cards, like ac and damage resistances/immunities/vulnerabilities. +
      • +
      • *NEW* Extended tooltip information for rolled dice.
      • +
      • *NEW* Option to display roll results to players. +
          +
        • Choose whether to always make GM attack cards public or not. Per default the rolls will *not* be shown to players.
        • +
        • If the card is publicly rolled you can choose to show the rolled results to your players. They will not be able to see the formula or dice rolled, only the total result.
        • +
        • Using this your players can easily apply damage to themself, without getting to much information about the attackers stats.
        • +
        +
      • +
      • *NEW* Contextmenu to apply damage to target/tokens
      • +
      • Fixed Tidy5eSheet saving throws rolling mess-style as well as displaying the default roll popup. Now only mess-style is chosen.
      • +

      v0.9.0

      • NEW FEATURE: Smooth(er) drag animations in the sidebar directories and on most actor sheets!
      • diff --git a/img/attack-card-example.png b/img/attack-card-example.png index 0296f6e..c4c754f 100644 Binary files a/img/attack-card-example.png and b/img/attack-card-example.png differ diff --git a/img/context-menu.png b/img/context-menu.png new file mode 100644 index 0000000..5fdfbf5 Binary files /dev/null and b/img/context-menu.png differ diff --git a/package.json b/package.json index 7b97fbb..c89442c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "module-template", - "version": "0.9.0", + "version": "0.10.0", "description": "", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/src/assets/ATTRIBUTION.md b/src/assets/ATTRIBUTION.md new file mode 100644 index 0000000..e6a568c --- /dev/null +++ b/src/assets/ATTRIBUTION.md @@ -0,0 +1,4 @@ +``shield-impact.svg`` is from [Delapouite](http://delapouite.com/) and is licensed under CC BY 3.0. [[Source](https://game-icons.net/1x1/delapouite/shield-impact.html)] +``shield-reflect.svg`` is from [Lorc](http://lorcblog.blogspot.com/) and is licensed under CC BY 3.0. [[Source](https://game-icons.net/1x1/lorc/shield-reflect.html)] +``cracked-shield.svg`` is from [Lorc](http://lorcblog.blogspot.com/) and is licensed under CC BY 3.0. [[Source](https://game-icons.net/1x1/lorc/cracked-shield.html)] +``shoulder-armor.svg`` is from [Delapouite](http://delapouite.com/) and is licensed under CC BY 3.0. [[Source](https://game-icons.net/1x1/delapouite/shoulder-armor.html)] diff --git a/src/assets/broken-shield.svg b/src/assets/broken-shield.svg new file mode 100644 index 0000000..fbdaa0e --- /dev/null +++ b/src/assets/broken-shield.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/shield-impact.svg b/src/assets/shield-impact.svg new file mode 100644 index 0000000..726c4a7 --- /dev/null +++ b/src/assets/shield-impact.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/shield-reflect.svg b/src/assets/shield-reflect.svg new file mode 100644 index 0000000..2f08135 --- /dev/null +++ b/src/assets/shield-reflect.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/shoulder-armor.svg b/src/assets/shoulder-armor.svg new file mode 100644 index 0000000..c55f60a --- /dev/null +++ b/src/assets/shoulder-armor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/css/mess.less b/src/css/mess.less index a37a588..ca50bf0 100644 --- a/src/css/mess.less +++ b/src/css/mess.less @@ -5,10 +5,15 @@ .mess-attack-card { --border-color: #444; + --border-button: #444; --font-sub-color: #9c9c9c; - --color-success: #18520b; - --color-fail: #aa0200; + + // --color-success: #18520b; + // --color-fail: #aa0200; + --color-success: #26a30a; + --color-fail: #c40502; + --font-color: #ccc; // check } } @@ -19,7 +24,11 @@ .mess-attack-card { --border-color: #FFF; + --border-button: #999; --font-sub-color: #33322e; + --color-success: #26a30a; + --color-fail: #c40502; + --font-color: #191813; } #chat-controls { @@ -126,7 +135,7 @@ } } -.mess #chat-log { +.mess { // Hide useless buttons :D button[data-action="attack"] ~ button[data-action="damage"], button[data-action="versatile"] { @@ -139,43 +148,123 @@ justify-content: space-between; align-items: flex-start; - button, .mess-dice-result { - cursor: pointer; - background: rgba(0, 0, 0, 0.1); - border: 2px groove var(--border-color); - height: 1.5em; - line-height: 1.2em; - width: 70%; - border-radius: 0.3em; + #context-menu { + max-width: 150px; + width: 150px; + min-width: 150px; + text-align: left; + left: calc(50% - 75px); + .mess-context-menu-header { + padding: 0.3em; + background: #aaa3; + i { + padding: 0 3px 0 0; + width: 1.3ch; + } + cursor: initial; + } + .context-item { + line-height: 1.5em; + padding: 0 1.5ch; + display: flex; + flex-flow: row-reverse nowrap; + span { + text-align: right; + flex: 1; + font-size: 1.2em; + cursor: pointer; + } + label { + cursor: pointer; + flex: 0; + color: #aaa; + } + } } .mess-dice-result { height: auto; - span { + .mess-roll-container { font-weight: bold; font-size:1.4em; + height: 0.8em; + + .mess-roll-total { + font-size: inherit; + } } .crit { color: var(--color-success); - color: #26a30a; + // color: #26a30a; } .fumble { color: var(--color-fail); - color: #c40502; + // color: #c40502; } - } - .dice-roll { - background: rgba(0, 0, 0, 0.1); - border: 2px groove var(--border-color); - height: 1auto; - line-height: 1.2em; - width: 70%; + #context-menu { + min-width: unset; + } } + } + .mess-button-to-hit, .mess-button-dmg, .mess-dice-result { + cursor: pointer; + background: rgba(0, 0, 0, 0.1); + // border: 2px groove var(--border-color); + border: 1px solid var(--border-button); + box-shadow: 0 0 2px #FFF inset; + height: 1.5em; + line-height: 1.2em; + width: 70%; + border-radius: 0.3em; + } + + .mess-hit, .mess-miss { + position: relative; + &:after { + background-color: var(--font-color); + width: 1.2em; + height: 1.2em; + position: absolute; + + right: 0; + content: ''; + display: inline-block; + filter: saturate(80%); + } + &.crit:after{ + background-color: var(--color-success); + } + &.fumble:after { + background-color: var(--color-fail); + } + } + + .mess-attack-card .dice-roll { + background: rgba(0, 0, 0, 0.1); + // border: 2px groove var(--border-color); + + border: 1px solid var(--border-button); + box-shadow: 0 0 2px #FFF inset; + height: auto; + line-height: 1.2em; + width: 70%; + } + .mess-hit:after { + mask: url(../assets/shield-impact.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shield-impact.svg) no-repeat 50% 50%; + bottom: 0px; + } + .mess-miss:after { + mask: url(../assets/shield-reflect.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shield-reflect.svg) no-repeat 50% 50%; + transform: scale(-1,1); + bottom: -3px; + } .mess-attack-flavor { flex: 1 100%; border-top: 2px groove var(--border-color); @@ -191,23 +280,102 @@ } .mess-chat-target { flex: 40%; + max-width: 40%; border-right: 2px groove var(--border-color); - + display: block; - span { + .mess-target-name { text-overflow: ellipsis; - font-size: 0.8em; + white-space: nowrap; + // font-size: 0.8em; } img { + z-index: 1; border: none; cursor: pointer; - + height: 75px; + display: block; + margin: 0 auto; &:hover { border: 1px solid var(--highlight-color); } } + + .mess-icon { + position: relative; + min-width: 1.2em; + min-height: 1.2em; + display: inline-block; + &:before { + background-color: var(--font-color); + width: 1.8em; + height: 1.8em; + position: absolute; + + right: 0; + content: ''; + display: inline-block; + filter: saturate(80%); + } + + &.ac:before { + mask: url(../assets/shoulder-armor.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shoulder-armor.svg) no-repeat 50% 50%; + } + + &.dam-res:before { + mask: url(../assets/shield-impact.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shield-impact.svg) no-repeat 50% 50%; + left: -0.3em; + } + + &.dam-inv:before { + mask: url(../assets/shield-reflect.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/shield-reflect.svg) no-repeat 50% 50%; + transform: scale(-1,1); + bottom: -0.5em; + left: -0.4em; + } + + &.dam-vuln:before { + mask: url(../assets/broken-shield.svg) no-repeat 50% 50%; + -webkit-mask: url(../assets/cracked-shield.svg) no-repeat 50% 50%; + left: -0.5em; + height: 1.5em; + } + } } + + .mess-target-overlay { + display: flex; + flex-flow: column nowrap; + align-items: flex-end; + // height: 0; + // overflow: visible; + // color: #ddd; + + overflow: hidden; + text-overflow: ellipsis; + .mess-target-name { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + } + + .mess-target-overlay-container { + // position: relative; + // z-index: 2; + // background: rgba(31, 31, 31, 0.5); + // box-shadow: 0 0 1em inset rgba(0,0,0, 1); + // padding: 0.5em; + width: fit-content; + max-width: 100%; + display: flex; + flex-flow: column nowrap; + } + } + .mess-chat-rolls { flex: 60%; display: block; @@ -217,10 +385,11 @@ .mess-chat-to-hit, .mess-chat-dmg { width: 100%; text-align: center; - display: flex; + flex-flow: column; align-items: center; justify-content: flex-start; + display: flex; } .mess-chat-sub-label { @@ -234,9 +403,157 @@ .mess-chat-roll-type { margin: 0.5em 0 0 0; + width: 100%; } } +.mess-is-gm { + .mess-gm-info { + display: block; + // margin-top: 0.5em; + border-top: 1px solid #aaa; + // padding: 0.5em 0 0 0; + width: 100%; + list-style-type: none; + label { + font-weight: bold; + } + li:not(:first-child) { + padding: 0.1em 0; + border-top: 1px dashed #aaa; + } + i { + font-size: 0.8em; + } + } + + .mess-gm-rolls { + .mess-chat-to-hit, .mess-chat-dmg { + display: flex; + } + .mess-show-btn { + display: block; + } + + .mess-show-players { + .mess-button-dmg, .mess-button-to-hit { + display: block; + &+label { + display: block; + } + } + + .mess-show-btn { + opacity: 1; + // i:before { + // content: "\f070"; + // } + } + } + + .dice-tooltip { + max-width: 100%; + overflow: hidden; + } + .dice { + display: block; + } + } +} + +.mess-gm-rolls { + .mess-chat-to-hit, .mess-chat-dmg { + display: none; + } + + .mess-show-players { + display: flex; + + .mess-dice-result { + display: flex; + } + + .mess-button-dmg, .mess-button-to-hit { + display: none; + &+label { + display: none; + } + } + } + + .dice-tooltip { + max-width: 0; + overflow: hidden; + } + .dice { + display: none; + } +} + +.mess-show-btn { + opacity: 0.5; + height: 0; + position: relative; + top: -1.25em; + align-self: flex-end; + display: none; + + i:before { + content: "\f06e"; + } +} + + +// .mess-chat-to-hit { +// .mess-gm-dice { +// display: block; +// .mess-roll-total { +// display: inline; +// } +// } +// } +// .mess-gm-dice { +// .dice-tooltip { +// max-width: 100%; +// overflow: hidden; +// } +// .dice { +// display: block; +// } + +// .mess-hit:after { +// right: -0.5em; +// } +// .mess-miss:after { +// right: -0.65em; +// } +// } +// } + +// .mess-chat-to-hit { +// .mess-gm-dice { +// .mess-roll-total { +// .mess-roll-total { +// display: none; +// } +// } +// } +// } +// .mess-gm-dice { +// .dice { +// display: none; +// } +// .dice-tooltip { +// max-width: 0; +// } +// .mess-hit:after, .mess-miss:after { +// right: calc(50% - 0.6em); +// } +// } +.mess-gm-info { + display: none; +} + .dnd5e.actor { .ability-mod, .ability-save { cursor: pointer; @@ -258,6 +575,53 @@ } } -.mess-popup-div { - -} \ No newline at end of file + +// .mess-gm-dice { +// display: none; +// &+label { +// display: none; +// } +// } + +// .mess-show-btn { +// opacity: 0.5; +// height: 0; +// position: relative; +// top: -1.25em; +// align-self: flex-end; +// } + +// .mess-chat-to-hit, .mess-chat-dmg { +// &.mess-show-players { +// display: flex; +// } +// } +// .mess-show-players { +// .mess-show-btn { +// display: none; +// opacity: 1; +// } + +// .mess-dice-result.mess-gm-dice { +// display: block; + +// &+label { +// display: block; +// } +// } +// } + +// .mess-is-gm { +// .mess-chat-to-hit, .mess-chat-dmg { +// display: flex; +// } +// .mess-show-btn { +// display: inline; +// } +// .mess-button-to-hit, .mess-button-dmg { +// display: block; +// &+label { +// display: block; +// } +// } +// } \ No newline at end of file diff --git a/src/lang/de.json b/src/lang/de.json index 4bf175d..7d250e1 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -74,6 +74,10 @@ "maxCrit": { "label": "Maximiere kritische Treffer.", "hint": "Ändert das Verhalten kritischer Schadenswürfe, indem der Wurf des kritischen Extrawürfel maximiert wird!" + }, + "mode": { + "activate": "GM Angriffskarte immer allen Spielern zeigen, egal welcher Würfelmodus gewählt wurde.", + "hint": "Wenn diese Option aktiviert ist werden Angriffskarten vom GM immer allen Spielern angezeigt, wobei nur grundlegende Informationen wie Angreifer und Ziel angezeigt werden. Der GM kann dann entscheiden gewürfelte Würfe den Spielern zu zeigen indem auf das Augensymbol auf der Angriffskarte geklickt wird. Hierbei werden nur die finalen Würfelergebnisse angezeigt und keine Details, wie geworfene Würfe oder Modifikatoren." } } }, diff --git a/src/lang/en.json b/src/lang/en.json index 2869147..64aadd0 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -5,7 +5,23 @@ "cardTargetTooltip": "Double click to pan to token, if its scene is currently viewed.", "chatCardResourceError": "There were not enough resources left for this attack!", "toHit": "To Hit", - "damage": "Damage" + "damage": "Damage", + "contextMenu": { + "dmg": "Damage", + "healing": "Healing", + "full": "Full", + "half": "Half", + "double": "Double", + "error": "You must select at least one token to apply the damage to!", + "applyTarget": "Apply to target", + "applySelected": "Apply to selected" + }, + "applyDamage": { + "sceneNotFound": "Scene for Target not found!", + "targetNotFound": "Target token not found!", + "targetNotOwner": "You are not allowed to modify target tokens HP!" + }, + "showToPlayers": "Toggle whether rolled dice results are shown to players. " }, "rollConfig": { "Advantage": "Roll with Advantage!", @@ -74,6 +90,10 @@ "maxCrit": { "label": "Use maximum crits.", "hint": "Changes behaviour of critical damage rolls to maximize the damage of the extra dice!" + }, + "mode": { + "activate": "Always display GM attack cards to all users, regardless of the roll mode.", + "hint": "This will always display the attack card with some basic information, like the attacker and the target to all players. The GM then can manually decide to show the rolled results on demand by clicking on the eye button next to the section titles on the card." } } }, diff --git a/src/lang/ja.json b/src/lang/ja.json index f672796..5e559b3 100644 --- a/src/lang/ja.json +++ b/src/lang/ja.json @@ -81,7 +81,11 @@ "placeables":{ "label": "オブジェクト操作の変更", "hint": "コマ、タイル、壁等のマウスで動かせるすべてのオブジェクトのドラッグ中の動作を変更します。マウスを早く動かしているときはマス目に沿わず、マイスを遅く動かしたときに沿うようになります。この機能によりオブジェクトのドラッグをやめる前からどこに沿っていくのかを予測することが可能です。" - } + }, + "draggableLists": { + "label": "フォルダ/アイテムドラッグ地点予測アニメーションを使用する", + "hint": "ドラッグ&ドロップを行うときにドロップした場所がどこになるのかをなめらかなアニメーションで表示します。" + } } }, "reloadReminder": { diff --git a/src/module.json b/src/module.json index f422037..dd489f9 100644 --- a/src/module.json +++ b/src/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.9.0", + "version": "0.10.0", "minimumCoreVersion": "0.6.0", "compatibleCoreVersion": "0.6.2", "author": "Moerill", @@ -26,10 +26,15 @@ { "lang": "ja", "name": "日本語", - "path": "lang/jp.json" + "path": "lang/ja.json" + }, + { + "lang": "pt-BR", + "name": "Português (Brasil)", + "path": "lang/pt-BR.json" } ], "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.9.0/mess.zip" + "download": "https://github.com/Moerill/mess/releases/download/v0.10.0/mess.zip" } \ No newline at end of file diff --git a/src/scripts/draggable-lists/draggable-list.js b/src/scripts/draggable-lists/draggable-list.js index 6a37188..c9a9034 100644 --- a/src/scripts/draggable-lists/draggable-list.js +++ b/src/scripts/draggable-lists/draggable-list.js @@ -10,7 +10,8 @@ export class DraggableList { get defaultOptions() { return { offset: 21, // in px - time: 0.1, // in seconds + time: 0.2, // in seconds + dir: null, // this selector will not get offset when hovered over // folowing are not used onDragStart: null, onDragEnd: null, @@ -20,7 +21,8 @@ export class DraggableList { } async _init() { - this._items = Array.from(this.container.childNodes).filter(e => e.matches && e.matches(this.selector)); + // this._items = Array.from(this.container.childNodes).filter(e => e.matches && e.matches(this.selector)); + this._items = Array.from(this.container.querySelectorAll(this.selector)); this.container.addEventListener('dragleave', ev => { const rect = this.container.getBoundingClientRect(); const boundary = 5; @@ -45,14 +47,13 @@ export class DraggableList { * => We search for the first offset element and define that one as target. Since default for sorting is "insertBefore", and most implementations i've found build upon FVTT default/dnd5e systems default, this is a rather save implementation to find the real target. */ this.container.addEventListener('drop', ev => { - const insideChild = ev.insideChild; - if (this._over && !insideChild) { - Object.defineProperty(ev, 'target', {writable: false, value: this._over}) - // Make sure that outer directories are not overwriting this stuff! - ev.insideChild = true; - } + // const insideChild = ev.insideChild; + // if (this._over) { + // Object.defineProperty(ev, 'target', {writable: false, value: this._over}) + // // Make sure that outer directories are not overwriting this stuff! + // // ev.insideChild = true; + // } }); - this._items.forEach((e, idx) => this._initItem(e, idx)) } @@ -62,39 +63,102 @@ export class DraggableList { el.addEventListener('dragleave', ev => this._onDragLeaveItem(ev, idx)); el.addEventListener('dragend', ev => { // safety net if for some reasons the rerender is to slow or fails... - const srcElement = ev.currentTarget; - srcElement.style.opacity = null; - srcElement.style.height = null; - TweenLite.from(srcElement, this.options.time, {opacity:0, height:0}); + // const srcElement = ev.currentTarget; + // srcElement.style.opacity = null; + // srcElement.style.height = null; + // TweenLite.from(srcElement, this.options.time, {opacity:0, height:0}); this._resetOffsets(); }); - el.addEventListener('dragstart', ev => { - this._dragged = ev.currentTarget; - TweenLite.to(ev.currentTarget, this.options.time, {opacity: 0, height: 0}); - }) + // el.addEventListener('dragstart', ev => { + // this._dragged = ev.currentTarget; + // // if (ev.currentTarget.matches(this.dir)) + // // this._draggingDir = true; + // // else + // // this._draggingDir = false; + // // TweenLite.to(ev.currentTarget, this.options.time, {opacity: 0, height: 0}); + // }) } _onDragEnterItem(ev, idx) { + console.time('dragEnter') ev.stopPropagation(); - // save the current target - this._over = this._items[idx]; + // // save the current target const time = this.options.time; const offset = this.options.offset; - const resetList = this._items.slice(0, idx); - TweenLite.to(resetList, time, {top: 0}); - const offsetList = this._items.slice(idx); - TweenLite.to(offsetList, time, {top: offset}); + this._resetOffsets(); + let target = ev.currentTarget; + console.log(ev.currentTarget) + // if inside a container, just make sure that its not jumping around. (Sometimes its ignoring the real target...) + if (target.matches(this.options.dir)) { + const items = Array.from(target.querySelectorAll(this.selector)); + const headerRect = target.getBoundingClientRect(); + if (ev.clientY > headerRect.top && ev.clientY < headerRect.bottom) { + TweenLite.to(items, time, {paddingTop: 0}); + this._over = target; + return; + } + for (let item of items) { + const rect = item.getBoundingClientRect(); + if (rect.bottom > ev.clientY) { + target = item; + break; + } + } + } + + this._over = target; + console.log(target, offset); + TweenLite.to(target, time, {paddingTop: offset}); + // let resetList = []; + // let offsetList = []; + // let prev = item.previousElementSibling; + // while (prev) { + // // TweenLite.to(prev, time, {top: 0}) + // resetList.push(prev); + // prev = prev.previousElementSibling; + // } + // let next = item; + // while (next) { + // if (!this._draggingDir && next.matches(this.options.dir)) { + // // TweenLite.to(next, time, {top: 0}); + // resetList.push(next); + // } else + // offsetList.push(next); + // // TweenLite.to(next, time, {top: offset}); + // next = next.nextElementSibling; + // } + // // // if (ev.currentTarget.matches(this.options.dir)) { + + // // // idx = idx + 1; + // // // } + // // // for (let i = idx; i < this._items.length; i++) { + // // // const rect = this._items[i].getBoundingClientRect(); + // // // } + // // const resetList = this._items.slice(0, idx); + // TweenLite.to(resetList, time, {top: 0}); + // // const offsetList = this._items.slice(idx); + // TweenLite.to(offsetList, time, {top: offset}); + + console.timeEnd('dragEnter') return false; } _onDragLeaveItem(ev, idx) { - // Empty for now? + // console.time('dragLeave') + ev.stopPropagation(); + return false; + // // Empty for now? + + // TweenLite.to(ev.currentTarget, 0.3, {transform: "scale(1, 1)"}); + // console.timeEnd('dragLeave') } + // We now work with padding, cause.. reasons.. _resetOffsets(time = this.options.time) { - TweenLite.to(this._items, time, {top: 0}); + // TweenLite.to(this._items, time, {top: 0}); + TweenLite.to(this._items, time, {paddingTop: 0}); } } diff --git a/src/scripts/draggable-lists/index.js b/src/scripts/draggable-lists/index.js index b96c69d..59599cb 100644 --- a/src/scripts/draggable-lists/index.js +++ b/src/scripts/draggable-lists/index.js @@ -5,8 +5,10 @@ export function initDraggableLists() { // Only needed for the "root" directory though.. :/ const oldFun = SidebarDirectory.prototype.activateListeners; SidebarDirectory.prototype.activateListeners = function(html) { - const list = html[0].querySelectorAll('.directory-list, .subdirectory'); - list.forEach(e => new DraggableList(e, '.entity')); + // const list = html[0].querySelectorAll('.directory-list, .directory-item'); + // list.forEach(e => new DraggableList(e, '.entity')); + const list = html[0].querySelector('.directory-list'); + new DraggableList(list, '.entity, .folder', {dir: '.folder'}); oldFun.call(this, html); } @@ -15,4 +17,27 @@ export function initDraggableLists() { const list = html[0].querySelectorAll('.item-list'); list.forEach(e => new DraggableList(e, '.item')); }); + + SceneDirectory.prototype._onLazyLoadImage = function(entries, observer) { + for ( let e of entries ) { + if ( !e.isIntersecting ) continue; + const li = e.target; + + // Background Image + if ( li.dataset.backgroundImage ) { + li.children[0].style["background-image"] = `url("${li.dataset.backgroundImage}")`; + delete li.dataset.backgroundImage; + } + + // Avatar image + const img = li.querySelector("img"); + if ( img && img.dataset.src ) { + img.src = img.dataset.src; + delete img.dataset.src; + } + + // No longer observe the target + observer.unobserve(e.target); + } + } } \ No newline at end of file diff --git a/src/scripts/prepared-spell-tracker.js b/src/scripts/prepared-spell-tracker.js index 5d0626f..fe296ae 100644 --- a/src/scripts/prepared-spell-tracker.js +++ b/src/scripts/prepared-spell-tracker.js @@ -35,9 +35,12 @@ export default async function addPreparedSpellTracker() { if (app.constructor.name === 'Tidy5eSheet') { const el = html[0].querySelector('.spellcasting-ability'); + if (!el) + return; el.appendChild(tracker, el); } else { const el = html[0].querySelector('.spellbook .inventory-list'); + if (!el) return; tracker.style.flex = '0'; tracker.style.alignSelf = 'flex-start'; tracker.style.margin = '0 8px'; diff --git a/src/scripts/rolls/apply-dmg.js b/src/scripts/rolls/apply-dmg.js index 33995ea..91e45d0 100644 --- a/src/scripts/rolls/apply-dmg.js +++ b/src/scripts/rolls/apply-dmg.js @@ -1,4 +1,167 @@ +function getAllDmg(li) { + const results = Array.from(li[0].parentNode.querySelectorAll('.mess-dice-result:not(.mess-versatile')).map(e => Number(e.querySelector('span').innerText)); + return results.reduce((a, b) => a + b, 0); +} + +function getAllVersatile(li) { + const resultArray = Array.from(li[0].parentNode.querySelectorAll('.mess-dice-result')); + if (resultArray.length > 1 && resultArray[1].classList.contains('mess-versatile')) + resultArray.splice(0,1) + const results = resultArray.map(e => Number(e.querySelector('span').innerText)); + return results.reduce((a, b) => a + b, 0); +} + export default function modifyApplyDmg() { + const canApply = (li) => true || canvas.tokens.controlled.length; + const canApplyNonVersatile = (li) => canApply(li) && li[0].parentNode.querySelector('.mess-dice-result:not(.mess-versatile)'); + const canApplyVersatile = (li) => canApply(li) && li[0].parentNode.querySelector('.mess-versatile'); + const hasTarget = (li) => !!li[0].closest('.mess-attack-card').dataset.targetId; + const hasNoTarget = (li) => !hasTarget(li); + const contextOptionsDmg = [ + { + name: game.i18n.localize("MESS.attackCard.contextMenu.applyTarget"), + condition: hasTarget + }, + { + name: game.i18n.localize("MESS.attackCard.contextMenu.applySelected"), + condition: hasNoTarget + }, + { + name: game.i18n.localize("MESS.attackCard.contextMenu.dmg"), + condition: canApplyNonVersatile, + icon: '' + }, + { + name: 'full', + icon: '', + condition: canApplyNonVersatile, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${getAllDmg(li)}` + }, + { + name: 'half', + icon: '', + condition: canApplyNonVersatile, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${Math.max(Math.floor(getAllDmg(li) * 0.5), 1)}` + }, + { + name: 'double', + icon: '', + condition: canApplyNonVersatile, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${Math.floor(getAllDmg(li) * 2)}` + }, + { + name: game.i18n.localize("MESS.attackCard.contextMenu.healing"), + condition: canApplyNonVersatile, + icon: '' + }, + { + name: 'full', + icon: '', + condition: canApplyNonVersatile, + callback: (target, li) => applyDamage(li, -1), + content: (li) => `${getAllDmg(li)}` + }, + { + name: [game.i18n.localize('DND5E.Versatile'), ' - ', game.i18n.localize("MESS.attackCard.contextMenu.dmg")], + condition: canApplyVersatile, + icon: '' + }, + { + name: 'full', + icon: '', + condition: canApplyVersatile, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${getAllVersatile(li)}` + }, + { + name: 'half', + icon: '', + condition: canApplyVersatile, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${Math.max(Math.floor(getAllVersatile(li) * 0.5), 1)}` + }, + { + name: 'double', + icon: '', + condition: canApplyVersatile, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${Math.floor(getAllVersatile(li) * 2)}` + }, + { + name: [game.i18n.localize('DND5E.Versatile'), ' - ', game.i18n.localize("MESS.attackCard.contextMenu.healing")], + condition: canApplyVersatile, + icon: '' + }, + { + name: 'full', + icon: '', + condition: canApplyVersatile, + callback: (target, li) => applyDamage(li, -1), + content: (li) => `${getAllVersatile(li)}` + } + ] + + + const contextOptionsRoll = [ + { + name: game.i18n.localize("MESS.attackCard.contextMenu.applyTarget"), + condition: hasTarget + }, + { + name: game.i18n.localize("MESS.attackCard.contextMenu.applySelected"), + condition: hasNoTarget + }, + { + name: game.i18n.localize("MESS.attackCard.contextMenu.dmg"), + condition: canApply, + icon: '' + }, + { + name: 'full', + icon: '', + condition: canApply, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${li[0].querySelector('span').innerText}` + }, + { + name: 'half', + icon: '', + condition: canApply, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${Math.max(Math.floor(Number(li[0].querySelector('span').innerText) * 0.5), 1)}` + }, + { + name: 'double', + icon: '', + condition: canApply, + callback: (target, li) => applyDamage(li, 1), + content: (li) => `${Math.floor(Number(li[0].querySelector('span').innerText) * 2)}` + }, + { + name: game.i18n.localize("MESS.attackCard.contextMenu.healing"), + condition: canApply, + icon: '' + }, + { + name: 'full', + icon: '', + condition: canApply, + callback: (target, li) => applyDamage(li, -1), + content: (li) => `${li[0].querySelector('span').innerText}` + } + ] + + + ContextMenu.prototype.bind = Function('"use strict"; return ( function ' + ContextMenu.prototype.bind.toString().replace(/this\.render\(parent\)/,`this.render(parent, event)`) + ')')(); + + Hooks.on('renderChatLog', (app, html, options) => { + new MessContextMenu(html.find('#chat-log'), ".mess-chat-dmg .mess-dice-result", contextOptionsRoll); + new MessContextMenu(html.find('#chat-log'), ".mess-chat-dmg .mess-chat-roll-type", contextOptionsDmg); + }) + Hooks.on('getChatLogEntryContext', function (html, options) { setProperty(game, 'mess.chatLogEntryContextOptions', options); @@ -8,4 +171,107 @@ export default function modifyApplyDmg() { for (let i = 1; i < options.length; i++) options[i].condition = canApply; }); +} + +class MessContextMenu extends ContextMenu { + constructor(...args) { + super(...args); + + this._menuItems = this.menuItems; + } + + render(target, event) { + const filteredItems = this._menuItems.filter(item => { + if (!item.condition) return true; + if (!(item.condition instanceof Function)) return item.condition; + return item.condition(target); + }) + + if (filteredItems.length) event.stopPropagation(); + else return; + this.menuItems = filteredItems.map(e => { + if (e.content) + e.name = e.content(target); + return e; + }); + + let html = $("#context-menu").length ? $("#context-menu") : $(''); + let ol = $('
          '); + html.html(ol); + + const frag = document.createDocumentFragment(); + for (let item of this.menuItems) { + const li = document.createElement('li') + + if (item.name instanceof Array) + item.name = item.name.map(e => game.i18n.localize(e)).join(""); + + if (item.icon) + li.innerHTML = `${item.icon}${game.i18n.localize(item.name)}`; + else + li.innerHTML = game.i18n.localize(item.name); + + if (item.callback) { + li.addEventListener('click', ev => { + ev.preventDefault(); + ev.stopPropagation(); + item.callback(target, li); + this.close(); + }); + li.classList.add('context-item'); + } else + li.classList.add('mess-context-menu-header'); + + frag.appendChild(li); + } + + ol[0].appendChild(frag); + + // Append to target + this._setPosition(html, target); + + // Animate open the menu + return this._animateOpen(html); + } + + _setPosition(html, target) { + html.removeClass("expand-up expand-down"); + super._setPosition(html, target); + } +} + +async function applyDamage(target, sign) { + const amount = Number(target.querySelector('span').innerText); + const card = target.closest('.mess-attack-card') + const targetId = card.dataset.targetId + if (targetId) { + const sceneId = card.dataset.sceneId; + const scene = game.scenes.get(sceneId); + if (!scene) { + ui.notifications.error(game.i18n.localize("MESS.attackCard.applyDamage.sceneNotFound")); + return; + } + const token = new Token(scene.getEmbeddedEntity("Token", targetId)); + if (!token) { + ui.notifications.error(game.i18n.localize("MESS.attackCard.applyDamage.targetNotFound")); + return; + } + if (!token.owner) { + ui.notifications.error(game.i18n.localize("MESS.attackCard.applyDamage.targetNotOwner")); + return; + } + const actor = token.actor; + actor.applyDamage(amount, sign); + } else { + const tokens = canvas.tokens.controlled; + if (!tokens.length) { + ui.notifications.error(game.i18n.localize('MESS.attackCard.contextMenu.error')); + return; + } + + for (const token of tokens) { + const a = token.actor; + a.applyDamage(amount, sign); + } + } } \ No newline at end of file diff --git a/src/scripts/rolls/controls.js b/src/scripts/rolls/controls.js index 2604f74..3651e7e 100644 --- a/src/scripts/rolls/controls.js +++ b/src/scripts/rolls/controls.js @@ -22,6 +22,9 @@ function registerSettings() { async function chatLogHook(app, html, data) { html[0].classList.add('mess'); + console.log("qwe", game.user, game.user.isGM) + if (game.user.isGM) + html[0].classList.add('mess-is-gm'); const div = document.createElement('div'); div.classList.add('mess-roll-control'); diff --git a/src/scripts/rolls/dice.js b/src/scripts/rolls/dice.js index 9cc103a..1759926 100644 --- a/src/scripts/rolls/dice.js +++ b/src/scripts/rolls/dice.js @@ -1,3 +1,5 @@ +import {getTargetToken, hasTarget} from './util.js'; + /** * All the functions provided here are heavily based on Foundrys DnD5e system, authored by Atropos. * Original repository: https://gitlab.com/foundrynet/dnd5e @@ -66,7 +68,7 @@ export async function rollD20(data) { 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); } @@ -150,7 +152,7 @@ export async function rollToHit(ev) { 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; + return false; } } // Get the Actor from a synthetic Token @@ -187,18 +189,54 @@ export async function rollToHit(ev) { 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.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'); + if (game.user.isGM) + div.classList.add('mess-gm-dice'); const span = div.appendChild(document.createElement('span')); - span.innerText = r.total; + span.classList.add('mess-roll-container'); + span.innerHTML = `${r.total}`; + div.insertAdjacentHTML('beforeend', await r.getTooltip()); + let customTooltip = ''; + const terms = rollData.terms.split('+'); // only adding + terms here anyway + const dataTerms = rollData.formula.split(/(?=[+-])/); + for (let i = 0; i < terms.length; i++) { + const term = terms[i]; + const num = Number(dataTerms[i]); + if (isNaN(num)) continue; + customTooltip += `
          +
          +

          + ${term} + ${num >= 0 ? '+'+num : num} +

          +
          +
          ` + } + div.querySelector('.dice-tooltip').insertAdjacentHTML('beforeend', customTooltip); + 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 (hasTarget(button)) { + // // Check if hit + // const target = getTargetToken(button); + // const ac = target.actor.data.data.attributes.ac.value; + // if (ac) { + // if ((r.total >= ac && d20 > fumble) || d20 >= crit) { + // span.classList.add('mess-hit'); + // } else { + // span.classList.add('mess-miss'); + // } + // } + // } + if (d20 >= crit) { span.classList.add('crit'); card.querySelector('.mess-chat-dmg .mess-chat-roll-type').innerHTML += ' - Crit!' @@ -285,7 +323,7 @@ export async function getDmgData({actor, item, spellLevel = null}) { 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') + else if (part[1] === 'versatile') part[1] = game.i18n.localize('DND5E.Versatile'); //evalute damage formula's for example "ceil(@classes.rogue.levels/2))d6" -> "4d6" @@ -307,6 +345,9 @@ export async function getDmgData({actor, item, spellLevel = null}) { * @param {Event} ev */ export async function rollDmg(ev) { + const contextMenu = ev.currentTarget.parentNode.querySelector('#context-menu'); + if (contextMenu) + contextMenu.remove(); // Extract card data const button = ev.currentTarget; button.disabled = true; @@ -318,7 +359,7 @@ export async function rollDmg(ev) { 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; + return false; } } const formula = button.dataset.formula; @@ -326,12 +367,40 @@ export async function rollDmg(ev) { 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.title = `${button.dataset.terms} = ${r.formula} = ${r.total}. Click to see rolls.`; div.classList.add('dice-roll'); - div.classList.add('mess-dice-result'); + div.classList.add('mess-dice-result'); + if (game.user.isGM) + div.classList.add('mess-gm-dice'); + + // small helper to detect if versatile dmg was rolled + const dmgType = button.nextElementSibling.innerText; + if (dmgType === game.i18n.localize('DND5E.Versatile')) + div.classList.add('mess-versatile'); + const span = div.appendChild(document.createElement('span')); - span.innerText = r.total; + span.classList.add('mess-roll-container'); + span.innerHTML = `${r.total}`; div.insertAdjacentHTML('beforeend', await r.getTooltip()); + let customTooltip = ''; + const terms = button.dataset.terms.split(/(?=[+-])/); + const dataTerms = formula.split(/(?=[+-])/).filter(e => e !== '+' && e !== '-'); // filter single + and - + // i really sh ould put this into a function............ + for (let i = 0; i < terms.length; i++) { + const term = terms[i]; + const num = Number(dataTerms[i].replace(/\s/g, '')); + console.log(dataTerms[i], num) + if (isNaN(num)) continue; + customTooltip += `
          +
          +

          + ${term} + ${num >= 0 ? '+'+num : num} +

          +
          +
          ` + } + div.querySelector('.dice-tooltip').insertAdjacentHTML('beforeend', customTooltip); const tooltip = div.childNodes[1]; tooltip.classList.add('hidden'); diff --git a/src/scripts/rolls/index.js b/src/scripts/rolls/index.js index 2265f6a..1508069 100644 --- a/src/scripts/rolls/index.js +++ b/src/scripts/rolls/index.js @@ -8,5 +8,5 @@ export function rolling() { return; rollConrolls(); applyDmg(); - modifyRolling() + modifyRolling(); } \ No newline at end of file diff --git a/src/scripts/rolls/modify-rolling.js b/src/scripts/rolls/modify-rolling.js index ba30981..73d984b 100644 --- a/src/scripts/rolls/modify-rolling.js +++ b/src/scripts/rolls/modify-rolling.js @@ -4,6 +4,12 @@ import {rollD20, getToHitData, rollToHit, getDmgData, rollDmg} from './dice.js'; export default function() { setupHooks(); game.mess.toggleItemBonusDamage = toggleItemBonusDamage; + + const oldGetTooltip = Roll.prototype.getTooltip; + Roll.prototype.getTooltip = function() { + for (let i = 0; i < this.parts.length; ) + return oldGetTooltip.call(this); + } } /** @@ -149,17 +155,55 @@ async function renderAttack(ev) { toHit: await getToHitData({actor, item}), dmgs: await getDmgData({actor, item, spellLevel}), sceneId: canvas.scene.id, - user: game.user.id + user: game.user.id, + isGM: game.user.isGM } 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 targetActor = target.actor ? target.actor.data : null; + let targetData = {}; + if (targetActor) { + targetData.ac = { + label: '', + title: game.i18n.localize('DND5E.ArmorClass'), + value: targetActor.data.attributes.ac.value + } + console.log(targetActor.data.traits) + let di = targetActor.data.traits.di.value.filter(e => e !== "custom").map(e => game.i18n.localize('DND5E.Damage' + e[0].toUpperCase() + e.substring(1))); + if (targetActor.data.traits.di.custom) + di.push(targetActor.data.traits.di.custom); + targetData.di = { + label: '', + title: game.i18n.localize('DND5E.DamImm'), + value: di.join(", ") + } + + let dr = targetActor.data.traits.dr.value.filter(e => e !== "custom").map(e => game.i18n.localize('DND5E.Damage' + e[0].toUpperCase() + e.substring(1))); + console.log(dr); + if (targetActor.data.traits.dr.custom) + dr.push(targetActor.data.traits.dr.custom); + targetData.dr = { + label: '', + title: game.i18n.localize('DND5E.DamRes'), + value: dr.join(", ") + } + let dv = targetActor.data.traits.dv.value.filter(e => e !== "custom").map(e => game.i18n.localize('DND5E.Damage' + e[0].toUpperCase() + e.substring(1))); + if (targetActor.data.traits.dv.custom) + dv.push(targetActor.data.traits.dv.custom); + targetData.dv = { + label: '', + title: game.i18n.localize('DND5E.DamVuln'), + value: dv.join(", ") + } + // console.log(targetData) + } const attackTemplateData = { ...attackData, target: target.data, + targetData: targetData, flavor: getFlavor(item.data.data.chatFlavor, target), allowed }; @@ -181,8 +225,13 @@ async function renderAttack(ev) { alias: item.actor.name } }; - if ( ["gmroll", "blindroll"].includes(rollMode) ) chatData["whisper"] = ChatMessage.getWhisperIDs("GM"); - if ( rollMode === "blindroll" ) chatData["blind"] = true; + if (!game.user.isGM || !game.settings.get('mess', 'attack-card-always-public')) { + const rollMode = game.settings.get("core", "rollMode"); + if ( ["gmroll", "blindroll"].includes(rollMode) ) chatData.whisper = ChatMessage.getWhisperRecipients("GM"); + // doesn't work anyway since its no "real" roll.... + // if ( rollMode === "blindroll" ) chatData.blind = true; + if ( rollMode === "selfroll" ) chatData.whisper = [game.user.id]; + } ChatMessage.create(chatData); } @@ -258,9 +307,10 @@ async function actorSheetHook(app, html, data) { data.title = game.i18n.format("DND5E.AbilityPromptTitle", {ability: label}); rollD20.bind(app.object)(data); - return true; + return false; })); const saveMods = html[0].querySelectorAll('.ability-save'); + $(saveMods).off(); saveMods.forEach(e => e.addEventListener('click', function(ev) { ev.stopPropagation(); ev.preventDefault(); @@ -285,6 +335,7 @@ async function actorSheetHook(app, html, data) { data.title = game.i18n.format("DND5E.SavePromptTitle", {ability: label}); data.parts = parts; rollD20.bind(app.object)(data); + return false; })); const skills = html[0].querySelectorAll('.skill-name'); @@ -329,6 +380,8 @@ function chatListeners(html) { html.on('click', '.mess-button-to-hit', rollToHit); html.on('click', '.mess-button-dmg', rollDmg); + + html.on('click', '.mess-show-btn', onToggleShowPlayers); } // Only overwrite stuff for attack buttons @@ -386,4 +439,14 @@ async function getTargetToken(ev) { const token = canvas.tokens.placeables.find(e => e.id === tokenId); if (!token) return false; return token; +} + +async function onToggleShowPlayers(ev) { + const div = ev.currentTarget.closest('.mess-chat-to-hit, .mess-chat-dmg'); + div.classList.toggle('mess-show-players'); + + const card = ev.currentTarget.closest('.mess-attack-card'); + const messageId = card.closest(".message").dataset.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/util.js b/src/scripts/rolls/util.js new file mode 100644 index 0000000..959df44 --- /dev/null +++ b/src/scripts/rolls/util.js @@ -0,0 +1,17 @@ +export function getTargetToken(el) { + + const card = el.closest('.mess-attack-card') + + const targetId = card.dataset.targetId + const sceneId = card.dataset.sceneId; + const scene = game.scenes.get(sceneId); + const token = new Token(scene.getEmbeddedEntity("Token", targetId)); + return token; +} + +export function hasTarget(el) { + const card = el.closest('.mess-attack-card'); + if (!card) return false; + if (card.dataset.targetId) return true; + return false; +} \ No newline at end of file diff --git a/src/scripts/settings.js b/src/scripts/settings.js index e6841c3..7e5ea10 100644 --- a/src/scripts/settings.js +++ b/src/scripts/settings.js @@ -53,6 +53,16 @@ export class MessSettings extends FormApplication { default: isDnD, type: Boolean }); + + game.settings.register('mess', 'attack-card-always-public', { + name: "Roll mode for alternative rolling.", + hint: "Always roll attack rolls public, with hidden rolls, but visible target.", + scope: "world", + config: false, + default: isDnD, + type: Boolean + }); + game.settings.register('mess', 'modify-templates', { name: "Activate modified templates.", @@ -177,6 +187,7 @@ export class MessSettings extends FormApplication { // data['better-draggable'] = game.settings.get('mess', 'better-draggable'); data['prepared-spell-tracker'] = game.settings.get('mess', 'prepared-spell-tracker'); data['add-scrolling'] = game.settings.get('mess', 'add-scrolling'); + data['attack-card-always-public'] = game.settings.get('mess', 'attack-card-always-public'); } return data; } diff --git a/src/templates/attack-card.html b/src/templates/attack-card.html index 1fc722a..b32e1b2 100644 --- a/src/templates/attack-card.html +++ b/src/templates/attack-card.html @@ -7,20 +7,31 @@ {{/unless}} {{#if target.img}}
          - {{localize 'MESS.attackCard.target'}}:
          {{target.name}}
          +
          +
          + {{!--localize 'MESS.attackCard.target'}}:
          --}}{{target.name}}
          +
            + {{#each targetData}} + {{#if this.value}} +
          • {{this.value}}
          • + {{/if}} + {{/each}} +
          +
          +
          {{/if}} -
          +
          {{#if toHit}}
          - - + +
          {{/if}} {{#if dmgs}}
          - + {{#each dmgs.parts}} diff --git a/src/templates/settings/dnd5e.html b/src/templates/settings/dnd5e.html index 78ee61f..6a960a7 100644 --- a/src/templates/settings/dnd5e.html +++ b/src/templates/settings/dnd5e.html @@ -19,6 +19,10 @@

          {{localize 'MESS.settings.dnd5e.rolling.header'}} {{localize 'MESS.settings.dnd5e.rolling.hint'}}

          +
          + + {{{localize 'MESS.settings.dnd5e.rolling.mode.hint'}}} +
          {{!-- does not exist atm --}} diff --git a/src/templates/welcome-screen.html b/src/templates/welcome-screen.html index 83b2246..7b9b564 100644 --- a/src/templates/welcome-screen.html +++ b/src/templates/welcome-screen.html @@ -8,6 +8,8 @@

          Thank you for using this Mess!

          +

          Please keep in mind that i am working on this and my other projects mostly alone, in my spare time and for free. Especially before complaining (rudely) about something not working or needing some time before getting done!

          +

          Stay polite and do your best to describe your issue, question or suggestion as detailed as possible, to help shorten time needed to clarify things and to even keep me motivated to react and go on working on this for free.

          Make sure to read the README, for important informations and 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. @@ -22,6 +24,23 @@

          Want to support development? Click here

          Changelog

          +

          v0.10.0

          +
            +
          • Redid the drag animations for a hopefully better experience. Please try this again if you previously had issues. If smth still doesn't work, please provide feedback on github.
          • +
          • *NEW* Portuguese localization thanks to GitHub user @rinnocenti.
          • +
          • *NEW* GM only information on attack cards. Added some useful information of the target to chat cards, like ac and damage resistances/immunities/vulnerabilities. +
          • +
          • *NEW* Extended tooltip information for rolled dice.
          • +
          • *NEW* Option to display roll results to players. +
              +
            • Choose whether to always make GM attack cards public or not. Per default the rolls will *not* be shown to players.
            • +
            • If the card is publicly rolled you can choose to show the rolled results to your players. They will not be able to see the formula or dice rolled, only the total result.
            • +
            • Using this your players can easily apply damage to themself, without getting to much information about the attackers stats.
            • +
            +
          • +
          • *NEW* Contextmenu to apply damage to target/tokens
          • +
          • Fixed Tidy5eSheet saving throws rolling mess-style as well as displaying the default roll popup. Now only mess-style is chosen.
          • +

          v0.9.0

          • NEW FEATURE: Smooth(er) drag animations in the sidebar directories and on most actor sheets!
          • diff --git a/webpack.config.js b/webpack.config.js index a754580..dc821ad 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,7 +5,7 @@ const webpack = require('webpack'); module.exports = { name: 'mess', entry: { - index: './src/scripts/index.js' + index: path.resolve(__dirname, 'src/scripts/index.js') }, mode: 'development', devtool: 'source-map',