v1.4.2
This commit is contained in:
@@ -30,7 +30,7 @@ let activeSounds = [];
|
||||
export let hotbarUses = false;
|
||||
export let calculateHotbarUses;
|
||||
|
||||
|
||||
let controlTokenTimer;
|
||||
|
||||
//CONFIG.debug.hooks = true;
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -116,7 +116,7 @@ async function analyzeWSmessage(msg){
|
||||
|
||||
if (action == 'token'){
|
||||
tokenControl.active = true;
|
||||
tokenControl.update(device,selectedTokenId,device);
|
||||
tokenControl.pushData(selectedTokenId,settings,context,device);
|
||||
}
|
||||
else if (action == 'move')
|
||||
move.update(settings,context,device);
|
||||
@@ -353,17 +353,13 @@ Hooks.once('ready', async()=>{
|
||||
}
|
||||
|
||||
const hotbarUsesTemp = game.modules.get("illandril-hotbar-uses");
|
||||
if (hotbarUsesTemp != undefined) {
|
||||
hotbarUses = true;
|
||||
}
|
||||
|
||||
if (hotbarUsesTemp != undefined) hotbarUses = true;
|
||||
});
|
||||
|
||||
Hooks.on('updateToken',(scene,token)=>{
|
||||
if (enableModule == false || ready == false) return;
|
||||
let tokenId = token._id;
|
||||
if (tokenId == selectedTokenId)
|
||||
tokenControl.update(selectedTokenId);
|
||||
if (tokenId == selectedTokenId) tokenControl.update(selectedTokenId);
|
||||
if (macroControl != undefined) macroControl.updateAll();
|
||||
});
|
||||
|
||||
@@ -373,8 +369,10 @@ Hooks.on('updateActor',(scene,actor)=>{
|
||||
for (let i=0; i<children.length; i++){
|
||||
if (children[i].actor.id == actor._id){
|
||||
let tokenId = children[i].id;
|
||||
if (tokenId == selectedTokenId)
|
||||
if (tokenId == selectedTokenId) {
|
||||
tokenControl.update(selectedTokenId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (macroControl != undefined) macroControl.updateAll();
|
||||
@@ -384,11 +382,17 @@ Hooks.on('controlToken',(token,controlled)=>{
|
||||
if (enableModule == false || ready == false) return;
|
||||
if (controlled) {
|
||||
selectedTokenId = token.data._id;
|
||||
tokenControl.update(selectedTokenId);
|
||||
if (controlTokenTimer != undefined) {
|
||||
clearTimeout(controlTokenTimer);
|
||||
controlTokenTimer = undefined;
|
||||
}
|
||||
}
|
||||
else {
|
||||
controlTokenTimer = setTimeout(function(){tokenControl.update(selectedTokenId);},10)
|
||||
selectedTokenId = undefined;
|
||||
}
|
||||
tokenControl.update(selectedTokenId);
|
||||
|
||||
if (macroControl != undefined) macroControl.updateAll();
|
||||
});
|
||||
|
||||
|
||||
32
changelog.md
32
changelog.md
@@ -1,8 +1,38 @@
|
||||
# Changelog Material Deck Module
|
||||
### v1.4.2 - 23-04-2021
|
||||
Fixes:
|
||||
<ul>
|
||||
<li>Last update I fixed the combat tracker, but this broke something in the Token Action (if you had a token selected, it would sometimes assumed you didn't have it selected), both should now work</li>
|
||||
<li>Token Action: Plugin wouldn't save text boxes (such as 'Prepend Title' or 'Custom') if they were empty</li>
|
||||
<li>Token Action: Improved performance, especially when 'Token' is set to 'Selected Token', and you're selecting a new token while you had another token selected<li>
|
||||
<li>Token Action => Stats => Skill Modifier: (dnd5e) Would only display the modifier, now it displays the total value (so with proficiency, if applicable)</li>
|
||||
<li>Combat Tracker => Mode: Function => Function: Would not always properly load the 'Turn Display' options</li>
|
||||
<li>Playlist Action: Background color would not show, and 'Off Color' wouldn't work for 'Offset'</li>
|
||||
<li>Macro Action => Macro Board => Offset: Background color would not show</li>
|
||||
<li>Scene Action => Offset: Background color would not show</li>
|
||||
</ul>
|
||||
|
||||
Additions:
|
||||
<ul>
|
||||
<li>Token Action: Changed the way how you can select what icon will be displayed. Instead of a true/false, there is now a selection box where you can select between 'None', 'Token Icon', 'Actor Icon' and 'Default', where the last one will display the default icon, for example the selected stat to display, the condition, etc</li>
|
||||
<li>Token Action => Stats & On Click => Custom: Textbox now automatically resizes to fit the content</li>
|
||||
<li>Token Action => On Click => Dice Roll: Added 'Roll Mode' option, where you can set to roll as 'default' (displays dialog), 'normal', 'advantage' or 'disadvantage'. All options, except for 'default', will ignore the previously added 'Token Roll Options' in 'Other Actions'</li>
|
||||
</ul>
|
||||
|
||||
Other:
|
||||
<ul>
|
||||
<li>Big code cleanup of the SD plugin</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<b>Compatible server app and SD plugin:</b><br>
|
||||
Material Server v1.0.2 (unchanged): https://github.com/CDeenen/MaterialServer/releases <br>
|
||||
SD plugin v1.4.2 (<b>must be updated!</b>): https://github.com/CDeenen/MaterialDeck_SD/releases<br>
|
||||
|
||||
### v1.4.1 - 21-04-2021
|
||||
Fixes:
|
||||
<ul>
|
||||
<li>Last update broke the combat tracker, should now be fixed</li>
|
||||
<li>Previous update broke the combat tracker, should now be fixed</li>
|
||||
</ul>
|
||||
|
||||
### v1.4.0 - 21-04-2021
|
||||
|
||||
BIN
img/other/.thumb/d20.png.jpg
Normal file
BIN
img/other/.thumb/d20.png.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
img/token/.thumb/temp_hp_empty.png.jpg
Normal file
BIN
img/token/.thumb/temp_hp_empty.png.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -2,8 +2,8 @@
|
||||
"name": "MaterialDeck",
|
||||
"title": "Material Deck",
|
||||
"description": "Material Deck allows you to control Foundry using an Elgato Stream Deck",
|
||||
"version": "1.4.1",
|
||||
"minimumSDversion": "1.4.0",
|
||||
"version": "1.4.2",
|
||||
"minimumSDversion": "1.4.2",
|
||||
"minimumMSversion": "1.0.2",
|
||||
"author": "CDeenen",
|
||||
"esmodules": [
|
||||
|
||||
@@ -28,6 +28,7 @@ export class CombatTracker{
|
||||
let txt = "";
|
||||
let background = "#000000";
|
||||
settings.combat = true;
|
||||
settings.icon = settings.displayIcon ? 'tokenIcon' : 'none';
|
||||
|
||||
if (mode == 'combatants'){
|
||||
if (MODULE.getPermission('COMBAT','DISPLAY_COMBATANTS') == false) {
|
||||
|
||||
@@ -51,6 +51,7 @@ export class MacroControl{
|
||||
|
||||
ringColor = (macroOffset == parseInt(this.offset)) ? ringOnColor : ringOffColor;
|
||||
ring = 2;
|
||||
src = "modules/MaterialDeck/img/transparant.png";
|
||||
}
|
||||
else { //Execute macro
|
||||
macroNumber += this.offset - 1;
|
||||
|
||||
@@ -52,6 +52,7 @@ export class PlaylistControl{
|
||||
const ringOffColor = settings.offRing ? settings.offRing : '#FF0000';
|
||||
const ringOnColor = settings.onRing ? settings.onRing : '#00FF00';
|
||||
const playlistType = settings.playlistType ? settings.playlistType : 'playStop';
|
||||
let src = "modules/MaterialDeck/img/transparant.png";
|
||||
|
||||
//Play/Stop
|
||||
if (playlistType == 'playStop'){
|
||||
@@ -74,7 +75,7 @@ export class PlaylistControl{
|
||||
else if (playlistType == 'offset') {
|
||||
let playlistOffset = parseInt(settings.offset);
|
||||
if (isNaN(playlistOffset)) playlistOffset = 0;
|
||||
if (playlistOffset == this.playlistOffset) ringColor = ringOnColor;
|
||||
ringColor = (playlistOffset == this.playlistOffset) ? ringOnColor : ringOffColor;
|
||||
}
|
||||
//Relative Offset
|
||||
else if (playlistType == 'relativeOffset') {
|
||||
@@ -87,7 +88,7 @@ export class PlaylistControl{
|
||||
const targetPlaylist = this.getPlaylist(number);
|
||||
if (targetPlaylist != undefined) name = targetPlaylist.name;
|
||||
}
|
||||
streamDeck.setIcon(context,device,"",{background:background,ring:2,ringColor:ringColor});
|
||||
streamDeck.setIcon(context,device,src,{background:background,ring:2,ringColor:ringColor});
|
||||
streamDeck.setTitle(name,context);
|
||||
}
|
||||
|
||||
@@ -98,6 +99,7 @@ export class PlaylistControl{
|
||||
const ringOffColor = settings.offRing ? settings.offRing : '#FF0000';
|
||||
const ringOnColor = settings.onRing ? settings.onRing : '#00FF00';
|
||||
const playlistType = settings.playlistType ? settings.playlistType : 'playStop';
|
||||
let src = "modules/MaterialDeck/img/transparant.png";
|
||||
|
||||
//Play/Stop
|
||||
if (playlistType == 'playStop'){
|
||||
@@ -130,12 +132,12 @@ export class PlaylistControl{
|
||||
else if (playlistType == 'offset') {
|
||||
let trackOffset = parseInt(settings.offset);
|
||||
if (isNaN(trackOffset)) trackOffset = 0;
|
||||
if (trackOffset == this.trackOffset) ringColor = ringOnColor;
|
||||
ringColor = (trackOffset == this.trackOffset) ? ringOnColor : ringOffColor;
|
||||
}
|
||||
//Relative Offset
|
||||
else if (playlistType == 'relativeOffset') {
|
||||
}
|
||||
streamDeck.setIcon(context,device,"",{background:background,ring:2,ringColor:ringColor});
|
||||
streamDeck.setIcon(context,device,src,{background:background,ring:2,ringColor:ringColor});
|
||||
streamDeck.setTitle(name,context);
|
||||
}
|
||||
|
||||
|
||||
@@ -114,8 +114,8 @@ export class SceneControl{
|
||||
else if (func == 'offset'){
|
||||
let offset = parseInt(settings.sceneOffset);
|
||||
if (isNaN(offset)) offset = 0;
|
||||
if (offset == this.sceneOffset) ringColor = ringOnColor;
|
||||
else ringColor = ringOffColor;
|
||||
ringColor = (offset == this.sceneOffset) ? ringOnColor : ringOffColor;
|
||||
src = "modules/MaterialDeck/img/transparant.png";
|
||||
}
|
||||
streamDeck.setTitle(name,context);
|
||||
streamDeck.setIcon(context,device,src,{background:background,ring:ring,ringColor:ringColor});
|
||||
|
||||
81
src/token.js
81
src/token.js
@@ -21,7 +21,7 @@ export class TokenControl{
|
||||
|
||||
async pushData(tokenId,settings,context,device,ring=0,ringColor='#000000'){
|
||||
const name = settings.displayName ? settings.displayName : false;
|
||||
const icon = settings.displayIcon ? settings.displayIcon : false;
|
||||
const icon = settings.icon ? settings.icon : 'none';
|
||||
const background = settings.background ? settings.background : "#000000";
|
||||
let stats = settings.stats ? settings.stats : 'none';
|
||||
const selection = settings.selection ? settings.selection : 'selected';
|
||||
@@ -30,7 +30,10 @@ export class TokenControl{
|
||||
|
||||
let validToken = false;
|
||||
let token;
|
||||
if (tokenId != undefined && tokenId != null) token = canvas.tokens.children[0].children.find(p => p.id == tokenId);
|
||||
if (settings.combatTrackerMode) {
|
||||
const mode = settings.combatTrackerMode;
|
||||
token = canvas.tokens.children[0].children.find(p => p.id == tokenId);
|
||||
}
|
||||
else if (selection == 'selected') token = canvas.tokens.controlled[0];
|
||||
else if (selection != 'selected' && tokenIdentifier == '') {}
|
||||
else if (selection == 'tokenName') token = canvas.tokens.children[0].children.find(p => p.name == tokenIdentifier);
|
||||
@@ -76,7 +79,8 @@ export class TokenControl{
|
||||
stats = 'none';
|
||||
}
|
||||
|
||||
if (icon) iconSrc = token.data.img;
|
||||
if (icon == 'tokenIcon') iconSrc = token.data.img;
|
||||
else if (icon == 'actorIcon') iconSrc = token.actor.data.img;
|
||||
if (name && stats != 'none' && stats != 'HPbox') txt += "\n";
|
||||
if (stats == 'custom'){
|
||||
const custom = settings.custom ? settings.custom : '';
|
||||
@@ -99,7 +103,7 @@ export class TokenControl{
|
||||
else if (game.system.id == 'dnd5e'){
|
||||
let attributes = token.actor.data.data.attributes;
|
||||
if (stats == 'HP') {
|
||||
if (!icon) {
|
||||
if (icon == 'stats') {
|
||||
uses = {
|
||||
available: attributes.hp.value,
|
||||
maximum: attributes.hp.max,
|
||||
@@ -119,7 +123,7 @@ export class TokenControl{
|
||||
else if (stats == 'TempHP') {
|
||||
const val = (attributes.hp.temp == null) ? 0 : attributes.hp.temp;
|
||||
const max = (attributes.hp.tempmax == null) ? 0 : attributes.hp.tempmax
|
||||
if (!icon) {
|
||||
if (icon == 'stats') {
|
||||
uses = {
|
||||
available: (attributes.hp.temp == null) ? 0 : attributes.hp.temp,
|
||||
maximum: (max == 0) ? 1 : attributes.hp.tempmax,
|
||||
@@ -186,7 +190,7 @@ export class TokenControl{
|
||||
}
|
||||
else if (stats == 'Skill') {
|
||||
const skill = settings.skill ? settings.skill : 'acr';
|
||||
const value = token.actor.data.data.skills?.[skill].mod;
|
||||
const value = token.actor.data.data.skills?.[skill].total;
|
||||
if (value >= 0) txt += '+';
|
||||
txt += value;
|
||||
}
|
||||
@@ -200,7 +204,7 @@ export class TokenControl{
|
||||
else if (game.system.id == 'D35E' || game.system.id == 'pf1'){
|
||||
let attributes = token.actor.data.data.attributes;
|
||||
if (stats == 'HP') {
|
||||
if (!icon) {
|
||||
if (icon == 'stats') {
|
||||
uses = {
|
||||
available: attributes.hp.value,
|
||||
maximum: attributes.hp.max,
|
||||
@@ -273,7 +277,7 @@ export class TokenControl{
|
||||
else if (game.system.id == 'pf2e'){
|
||||
let attributes = token.actor.data.data.attributes;
|
||||
if (stats == 'HP') {
|
||||
if (!icon) {
|
||||
if (icon == 'stats') {
|
||||
uses = {
|
||||
available: attributes.hp.value,
|
||||
maximum: attributes.hp.max,
|
||||
@@ -341,7 +345,7 @@ export class TokenControl{
|
||||
else if (game.system.id == 'demonlord'){
|
||||
let characteristics = token.actor.data.data.characteristics;
|
||||
if (stats == 'HP') {
|
||||
if (!icon) {
|
||||
if (icon == 'stats') {
|
||||
uses = {
|
||||
available: attributes.hp.value,
|
||||
maximum: attributes.hp.max,
|
||||
@@ -391,7 +395,7 @@ export class TokenControl{
|
||||
ring = 2;
|
||||
ringColor = "#FF7B00";
|
||||
}
|
||||
if (icon == false) {
|
||||
if (icon == 'stats') {
|
||||
iconSrc = window.CONFIG.controlIcons.visibility;
|
||||
overlay = true;
|
||||
}
|
||||
@@ -406,7 +410,7 @@ export class TokenControl{
|
||||
ring = 2;
|
||||
ringColor = "#FF7B00";
|
||||
}
|
||||
if (icon == false) {
|
||||
if (icon == 'stats') {
|
||||
iconSrc = window.CONFIG.controlIcons.combat;
|
||||
overlay = true;
|
||||
}
|
||||
@@ -417,7 +421,7 @@ export class TokenControl{
|
||||
ring = 2;
|
||||
ringColor = "#FF7B00";
|
||||
}
|
||||
if (icon == false) {
|
||||
if (icon == 'stats') {
|
||||
iconSrc = "fas fa-bullseye";
|
||||
}
|
||||
}
|
||||
@@ -429,9 +433,9 @@ export class TokenControl{
|
||||
ring = 1;
|
||||
if (game.system.id == 'dnd5e' || game.system.id == 'D35E' || game.system.id == 'pf1'){
|
||||
const condition = settings.condition ? settings.condition : 'removeAll';
|
||||
if (condition == 'removeAll' && icon == false)
|
||||
if (condition == 'removeAll' && icon == 'stats')
|
||||
iconSrc = window.CONFIG.controlIcons.effects;
|
||||
else if (icon == false) {
|
||||
else if (icon == 'stats') {
|
||||
let effect = CONFIG.statusEffects.find(e => e.id === condition);
|
||||
iconSrc = effect.icon;
|
||||
let effects = compatibleCore("0.8.1") ? token.actor.effects.contents : token.actor.effects.entries;
|
||||
@@ -444,9 +448,9 @@ export class TokenControl{
|
||||
}
|
||||
else if (game.system.id == 'pf2e') {
|
||||
const condition = settings.condition ? settings.condition : 'removeAll';
|
||||
if (condition == 'removeAll' && icon == false)
|
||||
if (condition == 'removeAll' && icon == 'stats')
|
||||
iconSrc = window.CONFIG.controlIcons.effects;
|
||||
else if (icon == false) {
|
||||
else if (icon == 'stats') {
|
||||
let effects = token.data.effects;
|
||||
for (let i=0; i<effects.length; i++){
|
||||
if (this.pf2eCondition(condition) == effects[i]){
|
||||
@@ -459,9 +463,9 @@ export class TokenControl{
|
||||
}
|
||||
else if (game.system.id == 'demonlord'){
|
||||
const condition = settings.condition ? settings.condition : 'removeAll';
|
||||
if (condition == 'removeAll' && icon == false)
|
||||
if (condition == 'removeAll' && icon == 'stats')
|
||||
iconSrc = window.CONFIG.controlIcons.effects;
|
||||
else if (icon == false) {
|
||||
else if (icon == 'stats') {
|
||||
let effect = CONFIG.statusEffects.find(e => e.id === condition);
|
||||
iconSrc = effect.icon;
|
||||
let effects = token.actor.effects.entries;
|
||||
@@ -485,7 +489,7 @@ export class TokenControl{
|
||||
overlay = true;
|
||||
const condition = settings.cubConditionName;
|
||||
if (condition == undefined || condition == '') return;
|
||||
if (icon == false) {
|
||||
if (icon == 'stats') {
|
||||
let effect = CONFIG.statusEffects.find(e => e.label === condition);
|
||||
iconSrc = effect.icon;
|
||||
let effects = compatibleCore("0.8.1") ? token.actor.effects.contents : token.actor.effects.entries;
|
||||
@@ -501,7 +505,7 @@ export class TokenControl{
|
||||
streamDeck.noPermission(context,device);
|
||||
return;
|
||||
}
|
||||
if (icon == false) return;
|
||||
if (icon != 'stats') return;
|
||||
const method = settings.wildcardMethod ? settings.wildcardMethod : 'iterate';
|
||||
let value = parseInt(settings.wildcardValue);
|
||||
if (isNaN(value)) value = 1;
|
||||
@@ -541,7 +545,7 @@ export class TokenControl{
|
||||
streamDeck.noPermission(context,device);
|
||||
return;
|
||||
}
|
||||
if (icon == false) {
|
||||
if (icon == 'stats') {
|
||||
iconSrc = window.CONFIG.controlIcons.visibility;
|
||||
ring = 2;
|
||||
overlay = true;
|
||||
@@ -552,14 +556,14 @@ export class TokenControl{
|
||||
streamDeck.noPermission(context,device);
|
||||
return;
|
||||
}
|
||||
if (icon == false) {
|
||||
if (icon == 'stats') {
|
||||
iconSrc = window.CONFIG.controlIcons.combat;
|
||||
ring = 2;
|
||||
overlay = true;
|
||||
}
|
||||
}
|
||||
else if (settings.onClick == 'target') { //target token
|
||||
if (icon == false) {
|
||||
if (icon == 'stats') {
|
||||
iconSrc = "fas fa-bullseye";
|
||||
ring = 2;
|
||||
overlay = true;
|
||||
@@ -574,21 +578,21 @@ export class TokenControl{
|
||||
const condition = settings.condition ? settings.condition : 'removeAll';
|
||||
if (condition == 'removeAll' && icon == false)
|
||||
iconSrc = window.CONFIG.controlIcons.effects;
|
||||
else if (icon == false)
|
||||
else if (icon == 'stats')
|
||||
iconSrc = CONFIG.statusEffects.find(e => e.id === condition).icon;
|
||||
}
|
||||
else if (game.system.id == 'pf2e') {
|
||||
const condition = settings.condition ? settings.condition : 'removeAll';
|
||||
if (condition == 'removeAll' && icon == false)
|
||||
if (condition == 'removeAll' && icon == 'stats')
|
||||
iconSrc = window.CONFIG.controlIcons.effects;
|
||||
else if (icon == false)
|
||||
else if (icon == 'stats')
|
||||
iconSrc = this.pf2eCondition(condition);
|
||||
}
|
||||
else if (game.system.id == 'demonlord'){
|
||||
const condition = settings.condition ? settings.condition : 'removeAll';
|
||||
if (condition == 'removeAll' && icon == false)
|
||||
if (condition == 'removeAll' && icon == 'stats')
|
||||
iconSrc = window.CONFIG.controlIcons.effects;
|
||||
else if (icon == false)
|
||||
else if (icon == 'stats')
|
||||
iconSrc = CONFIG.statusEffects.find(e => e.id === condition).icon;
|
||||
}
|
||||
ring = 1;
|
||||
@@ -601,7 +605,7 @@ export class TokenControl{
|
||||
}
|
||||
const condition = settings.cubConditionName;
|
||||
if (condition == undefined || condition == '') return;
|
||||
if (icon == false) {
|
||||
if (icon == 'stats') {
|
||||
iconSrc = CONFIG.statusEffects.find(e => e.label === condition).icon;
|
||||
}
|
||||
ring = 1;
|
||||
@@ -609,7 +613,7 @@ export class TokenControl{
|
||||
}
|
||||
}
|
||||
|
||||
if (icon == false){
|
||||
if (icon == 'stats'){
|
||||
if (MODULE.getPermission('TOKEN','STATS') == false) stats = statsOld;
|
||||
if (stats == 'HP') //HP
|
||||
iconSrc = "modules/MaterialDeck/img/token/hp_empty.png";
|
||||
@@ -851,12 +855,17 @@ export class TokenControl{
|
||||
const ability = settings.rollAbility ? settings.rollAbility : 'str';
|
||||
const skill = settings.rollSkill ? settings.rollSkill : 'acr';
|
||||
const save = settings.rollSave ? settings.rollSave : 'str';
|
||||
const rollOptions = otherControls.rollOption ? otherControls.rollOption : 'dialog';
|
||||
const options = {
|
||||
fastForward: (otherControls.rollOption != 'dialog'),
|
||||
advantage: (otherControls.rollOption == 'advantage'),
|
||||
disadvantage: (otherControls.rollOption == 'disadvantage')
|
||||
}
|
||||
const rollMode = settings.rollMode ? settings.rollMode : 'default';
|
||||
let options;
|
||||
if (rollMode == 'default')
|
||||
options = {
|
||||
fastForward: (otherControls.rollOption != 'dialog'),
|
||||
advantage: (otherControls.rollOption == 'advantage'),
|
||||
disadvantage: (otherControls.rollOption == 'disadvantage')
|
||||
}
|
||||
else if (rollMode == 'normal') options = {fastForward:true}
|
||||
else if (rollMode == 'advantage') options = {fastForward:true,advantage:true}
|
||||
else if (rollMode == 'disadvantage') options = {fastForward:true,disadvantage:true}
|
||||
|
||||
if (game.system.id == 'pf2e') {
|
||||
if (roll == 'ability') token.actor.data.data.saves?.[ability].roll(options);
|
||||
|
||||
Reference in New Issue
Block a user