
I've seen at least 1 recent post stating that 3D dice have stopped working when a dice roll is called from an API via sendChat(). Has anything been done about this? I'm getting the same issue, but I wonder if I'm doing something wrong. OS: Windows 10 Home 64-bit Build 18363 M/C: Intel Core i7-6700K CPU @4Ghx (no overclock) RAM: 16GB Graphics: NVIDIA GeForce GTX 1060 6GB Graphics driver: v27.21.14.5671 dated 9/30/2020 Browser: Google Chrome Version 87.0.4280.141 (Official Build) (64-bit) I've read the API Chat wiki entry and produced this piece of code to demonstrate the issue, which uses the {use3d: true} option. Loading this API Script and using the command !d3check --rolldice 1d4|test roll.|true should roll a d4 as a 3d dice roll. Use !d3check --help to see format of commands. /**
* diceCheck.js
*
* * Copyright 2020: Richard @ Damery.
* Licensed under the GPL Version 3 license.
* <a href="http://www.gnu.org/licenses/gpl.html" rel="nofollow">http://www.gnu.org/licenses/gpl.html</a>
*
* This script is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* The goal of this script is to demonstrate issues found with rolling
* 3d dice using sendChat() API function
*
* v0.001 23/12/2020 Initial creation from diceCheck.js
*
*/
var diceCheck = (function() {
'use strict';
var version = 0.001,
author = 'RED',
pending = null;
var PR_Enum = Object.freeze({
YESNO: 'YESNO',
CUSTOM: 'CUSTOM',
});
var MenuState = Object.freeze({
ENABLED: false,
DISABLED: true,
});
var flags = {
image: false,
archive: false,
dice3d: true,
// RED: v1.207 determine if ChatSetAttr is present
canSetAttr: true,
};
var design = {
grey_button: '"display: inline-block; background-color: lightgrey; border: 1px solid black; padding: 4px; color: dimgrey; font-weight: extra-light;"',
selected_button: '"display: inline-block; background-color: white; border: 1px solid red; padding: 4px; color: red; font-weight: bold;"',
boxed_number: '"display: inline-block; background-color: yellow; border: 1px solid blue; padding: 2px; color: black; font-weight: bold;"'
};
var diceCheck_tmp = (function() {
var templates = {
button: _.template('<a style="display: inline-block; font-size: 100%; color: black; padding: 3px 3px 3px 3px; margin: 2px 2px 2px 2px; border: 1px solid black; border-radius: 0.5em; font-weight: bold; text-shadow: -1px -1px 1px #FFF, 1px -1px 1px #FFF, -1px 1px 1px #FFF, 1px 1px 1px #FFF; background-color: #C7D0D2;" href="<%= command %>"><%= text %></a>'),
confirm_box: _.template('<div style="font-weight: bold; background-color: #FFF; text-align: center; box-shadow: rgba(0,0,0,0.4) 3px 3px; border-radius: 1em; border: 1px solid black; margin: 5px 5px 5px 5px; padding: 2px 2px 2px 2px;">'
+ '<div style="border-bottom: 1px solid black;">'
+ '<%= message %>'
+ '</div>'
+ '<table style="text-align: center; width: 100%">'
+ '<tr>'
+ '<td>'
+ '<%= confirm_button %>'
+ '</td>'
+ '<td>'
+ '<%= reject_button %>'
+ '</td>'
+ '</tr>'
+ '</table>'
+ '</div>')
};
return {
getTemplate: function(tmpArgs, type) {
var retval;
retval = _.find(templates, function(e,i) {
if (type === i) {
{return true;}
}
})(tmpArgs);
return retval;
},
hasTemplate: function(type) {
if (!type)
{return false;}
return !!_.find(_.keys(templates), function(elem) {
{return (elem === type);}
});
}
};
}());
/**
* Init
*/
var init = function() {
if (!state.diceCheck)
{state.diceCheck = {};}
if (!state.diceCheck.attrsToCreate)
{state.diceCheck.attrsToCreate = {};}
if (!state.diceCheck.debug)
{state.diceCheck.debug = false;}
log(`-=> diceCheck v${version} <=-`);
};
// ------------------------------------------------ Deal with in-line expressions --------------------------------
/**
* In the inline roll evaluator from ChatSetAttr script v1.9
* by Joe Singhaus and C Levett.
**/
var processInlinerolls = function (msg) {
if (msg.inlinerolls && msg.inlinerolls.length) {
return msg.inlinerolls.map(v => {
const ti = v.results.rolls.filter(v2 => v2.table)
.map(v2 => v2.results.map(v3 => v3.tableItem.name).join(", "))
.join(", ");
return (ti.length && ti) || v.results.total || 0;
})
.reduce((m, v, k) => m.replace(`$[[${k}]]`, v), msg.content);
} else {
return msg.content;
}
};
// -------------------------------------- make dice rolls and roll queries ------------------------------------
/**
* Use a callback from a sendChat dice roll to send the result as a --attkRoll command
* rollCmd must be a /roll or a /gmroll dice roll
* Alternatively, if rollCmd is anything else, the response will be processed for inline rolls
* and the full response returned as a --attkQuery command (useful for roll queries)
* If an array of args is supplied, these will be appended to the command string, each separated by '|'
* so that they are passed back to the handler
**/
var diceRoll = function( rollCmd, rollType, args ) {
log( 'diceRoll called');
var rollResult, content;
sendChat('',rollCmd,function(ops) {
log( 'dice callback made');
_.each( ops, function( resultObj ) {
if (resultObj.type == 'rollresult' || resultObj.type == 'gmrollresult') {
rollResult = JSON.parse(resultObj.content);
if (rollResult) {
content = '!attk --attkroll ' + rollType + '|' + rollResult.total;
for (let i=0; i<args.length; i++) {
content += '|' + args[i];
};
sendPublic( content );
};
} else if (resultObj.type != 'API' ) {
rollResult = processInlinerolls( resultObj );
if (rollResult) {
content = '!attk --attkroll ' + rollType + '|' + rollResult;
for (let i=0; i<args.length; i++) {
content += '|' + args[i];
};
sendPublic( content );
};
};
});
}, {use3d:true} );
};
// -------------------------------------------- send messages to chat -----------------------------------------
/**
* Send public message with 3d dice rolls (if enabled)
*/
var sendPublic = function(msg,dice3d,charCS) {
if (!msg)
{return undefined;}
var who,
roll3d;
if (charCS) {
who = 'character|'+charCS.id;
} else {
who = '';
}
roll3d = !!dice3d;
sendChat(who,msg,null,{use3d: roll3d});
};
/**
* Sends a response with a 3d dice roll (if enabled) to everyone who controls the character
*/
var sendDiceRoll = function(charCS,msg,as,img) {
if (!msg)
{return null;}
var to, content;
if (!charCS || charCS.get('controlledby').length == 0) {
to = '/w gm ';
} else {
to = '/w "' + charCS.get('name') + '" ';
}
content = to
+ '<div style="position: absolute; top: 4px; left: 5px; width: 26px;">'
+ '<img src="' + (img ? img:fields.feedbackImg) + '">'
+ '</div>'
+ msg;
sendChat((as ? as:fields.feedbackName),content,null,{noarchive:!flags.archive,use3d:flags.dice3d});
};
// -------------------------------------------- utility functions ----------------------------------------------
/**
* Find the GM, generally when a player can't be found
*/
var findTheGM = function() {
var playerGM,
players = findObjs({ _type:'player' });
if (players.length !== 0) {
if (!_.isUndefined(playerGM = _.find(players, function(p) {
var player = p;
if (player) {
if (playerIsGM(player.id)) {
return player.id;
}
}
}))) {
return playerGM.id;
}
}
return undefined;
}
// ------------------------------------------ Command Functions --------------------------------------------
/**
* Show help message
*/
var showHelp = function() {
var content =
'<div style="background-color: #FFF; border: 2px solid #000; box-shadow: rgba(0,0,0,0.4) 3px 3px; border-radius: 0.5em; margin-left: 2px; margin-right: 2px; padding-top: 5px; padding-bottom: 5px;">'
+ '<div style="font-weight: bold; text-align: center; border-bottom: 2px solid black;">'
+ '<span style="font-weight: bold; font-size: 125%">DiceCheck v'+version+'</span>'
+ '</div>'
+ '<div style="padding-left: 5px; padding-right: 5px; overflow: hidden;">'
+ '<div style="font-weight: bold;">'
+ '!d3check --help'
+ '</div>'
+ '<li style="padding-left: 10px;">'
+ 'Display this message'
+ '</li>'
+ '<br>'
+ '<div style="font-weight: bold;">'
+ '!d3check --rolldice roll|message|use3d'
+ '</div>'
+ '<li style="padding-left: 10px;">'
+ 'Roll dice using the sendChat() API function.'
+ '</li>'
+ '<li style="padding-left: 20px;">'
+ '<b>roll</b> this set of dice. Use the same format as used for \/roll.'
+ '</li>'
+ '<li style="padding-left: 20px;">'
+ '<b>message</b> to display describing the roll.'
+ '</li>'
+ '<li style="padding-left: 20px;">'
+ '<b>use3d</b> true or false. Passed to sendChat() as the value for the option key \'use3d\'.'
+ '</li>'
+ '<br>'
+ '</li>'
+ '<br>'
+ '</div>'
+ '</div>';
sendPublic(content);
};
/**
* Function to handle the internal --attkRoll command and return
* a dice roll to where it came from
**/
var doDiceRoll = function(args,playerId) {
args = args.split('|');
if (args.length < 2) {
sendPublic( 'Internal diceCheck roll error' );
showHelp();
return;
};
var content = '/roll ' + args[0] + ' ' + args[1] + ' Roll 3d is ',
roll3d = !!args[2] || false;
content += roll3d;
sendPublic( content, roll3d );
};
// -------------------------------------------------------- Event Handlers --------------------------------------------------
/**
* Handle chat message event
* RED: v1.213 Updated to allow multiple actions per call
* This allows procedural/linear processing of activity and overcomes
* some of the limitations of Roll20 asynchronous processing
*/
var handleChatMessage = function(msg) {
var args = processInlinerolls(msg),
senderId = msg.playerid,
selected = msg.selected;
if (args.indexOf('!d3check') !== 0)
{return;}
args = args.split(' --');
args.shift();
_.each(args, function(e) {
var arg = e;
senderId = msg.playerid;
if (_.isUndefined(senderId) || _.isUndefined(getObj('player',senderId))) {
if (_.isUndefined(senderId = findTheGM())) {
return;
}
};
if (msg.type === 'api') {
if (arg.indexOf('rolldice') === 0) {
arg = arg.replace('rolldice','').trim();
doDiceRoll(arg,senderId);
} else if (arg.indexOf('help') === 0) {
arg = arg.replace('help','').trim();
showHelp();
} else {
sendPublic('<span style="color: red;">Invalid command " <b>'+msg.content+'</b> "</span>');
showHelp();
}
}
});
};
// -------------------------------------------------------------- Register the API -------------------------------------------
/**
* Register and bind event handlers
*/
var registerAPI = function() {
on('chat:message',handleChatMessage);
};
return {
init: init,
registerAPI: registerAPI
};
}());
on("ready", function() {
'use strict';
diceCheck.init();
diceCheck.registerAPI();
});