See how this tickles your fancy... Commands: !sly <difficulty> <[[some roll]]> [optional content] <difficulty> is an integer to check against the number of even results <[[some roll]]> is an inline roll of dice which will be consulted for odd/even results. Only the first set of dice is considered, so use [[6d6]], not [[3d6+3d6]]. [optional content] is some collection of text to show along with the result, possibly to describe the action attempted, or things used, etc. Inline rolls are expanded, so "Attempts to swim with the aid of [[1d8]] ducks..." would result in something like "Attempts to swim with the aid of 3 ducks...". !wsly -- just like above, but whispers the output to the issuing player and the gm. !bsly -- just like above, but only whispers the results to the GM and notifies the player it was sent, along with the optional content. Examples: !sly 3 [[6d6]] Failure case (with despairs) Success case (with boons) Success case (balanced) !sly 3 [[5d10]] Attempts to swim with the aid of [[1d8]] ducks... !bsly 3 [[5d10]] Attempts to swim with the aid of [[1d8]] ducks... For the Boon and Despair images, I just grabbed some images off the internet that felt good. The URLs are near the top of the script, so should be easy enough to replace. The styles used are near the top as well, and should be easily adjusted as desired. Code: on('ready',function(){
'use strict';
const
image = {
boon: '<a href="http://www.realmoneyslotsonline.com/wp-content/uploads/2016/09/2000px-Clover_symbol.svg_.png" rel="nofollow">http://www.realmoneyslotsonline.com/wp-content/uploads/2016/09/2000px-Clover_symbol.svg_.png</a>',
dispair: '<a href="http://1.bp.blogspot.com/-NM95GPF2tTU/Tb34jIV6xgI/AAAAAAAAABk/HdC83ZSqGjU/s1600/gyen_lg.gif" rel="nofollow">http://1.bp.blogspot.com/-NM95GPF2tTU/Tb34jIV6xgI/AAAAAAAAABk/HdC83ZSqGjU/s1600/gyen_lg.gif</a>'
},
styles = {
body: {
'background-image': "url('<a href="http://static1.squarespace.com/static/538b5343e4b06b1e5375769c/t/5717e58bab48de61b52f9096/1461183887470/F71ddls.jpg" rel="nofollow">http://static1.squarespace.com/static/538b5343e4b06b1e5375769c/t/5717e58bab48de61b52f9096/1461183887470/F71ddls.jpg</a>')",
'background-repeat-y': 'no-repeat',
'background-position': 'top',
'background-size': 'auto 100%',
'background-color': '#DFBC98',
'border': '0',
'border-top-left-radius': '2em',
'border-top-right-radius': '2em',
'padding': '0'
},
result: {
'text-align': 'center',
'background-color': '#212122',
'border': '3px double white',
'border-top-left-radius': '1em',
'border-top-right-radius': '1em',
'font-size': '2em',
'line-height': '2em',
'font-weight': 'bold'
},
resultSuccess: {
'color': '#008000'
},
resultFailure: {
'color': '#8b0000'
},
content: {
'font-weight': 'bold',
'font-size': '1.2em',
'padding': '1em .5em .5em .5em',
'color': 'black',
'text-shadow': '1px 1px #666'
},
dice: {
'background-color': '#fffff0',
'border': '1px solid #333',
'border-radius': '.5em',
'max-width': '1.2em',
'max-height': '1.2em',
'height': '1.2em',
'width': '1.2em',
'text-align': 'center',
'margin': '.1em',
'font-weight': 'bold',
'display': 'inline-block'
},
diceOdd: {
'color': '#B22222'
},
diceEven: {
'color': '#228B22'
},
bdImage: {
'max-width': '2em',
'max-height': '2em'
},
bdDice: {
'border-width': '1px',
'border-style': 'solid',
'border-radius': '.75em',
'margin': '.2em',
'text-align': 'center',
'padding': '.5em',
'max-width': '2em',
'max-height': '2em',
'height': '2em',
'width': '2em',
'display': 'inline-block'
},
boon: {
'background-color': '#fffff0',
'border-color': '#daa520'
},
despair: {
'background-color': '#a52a2a',
'border-color': '#800000',
}
},
s = (o)=>_.map(o,(v,k)=>`${k}:${v};`).join(''),
cmdregex=/^(!([bw])?sly)\s*/,
getResults = function (roll,difficulty){
let res = {
rolls: _.pluck(roll.results.rolls[0].results,'v')
};
res.counts = _.countBy(res.rolls,(n)=> (n%2===0 ? 'even' : 'odd'));
res.counts.odd=(res.counts.odd||0);
res.counts.even=(res.counts.even||0);
res.success = res.counts.even >= difficulty;
res.boon = (res.counts.even > res.counts.odd ? res.counts.even - res.counts.odd : 0);
res.dispair = (res.counts.odd > res.counts.even ? res.counts.odd - res.counts.even : 0);
return res;
},
formatBDDice = function(res){
return _.times(res.boon,()=>`<div style="${s(styles.bdDice)}${s(styles.boon)}"><img style="${s(styles.bdImage)}" src="${image.boon}"></div>`).join('')+
_.times(res.dispair,()=>`<div style="${s(styles.bdDice)}${s(styles.despair)}"><img style="${s(styles.bdImage)}" src="${image.dispair}"></div>`).join('');
},
formatDice = function(res){
let odd =(n)=>n%2!==0;
return _.map(res.rolls, (r)=>`<div style="${s(styles.dice)}${odd(r) ? s(styles.diceOdd) : s(styles.diceEven) }">${r}</div>`).join('');
};
on('chat:message',function(msg){
if('api' === msg.type) {
let match=msg.content.match(cmdregex);
if(match){
let who=(getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'),
valid = (msg.content.split(/\s+/).length>2);
if(valid && _.has(msg,'inlinerolls')){
let rollIndex=msg.content.match(/\$\[\[(\d+)\]\]/)[1],
difficulty = msg.content.match(/(?:\s)\b(\d+)\b(?:\s|$)/)[1],
content = _.chain(msg.inlinerolls)
.reduce(function(m,v,k){
m['$[['+k+']]']=v.results.total || 0;
return m;
},{})
.reduce(function(m,v,k){
return m.replace(k,v);
},msg.content)
.value()
.replace(cmdregex,'')
.replace(/^\s*\d+\s+\d+\s*/,''),
results = getResults(msg.inlinerolls[rollIndex],difficulty),
output = `<div style="${s(styles.body)}">`+
`<div style="${s(styles.result)}${results.success ? s(styles.resultSuccess) : s(styles.resultFailure)}">`+
(results.success ? 'Success' : 'Failure') +
`</div>`+
`<div>${formatBDDice(results)}</div>`+
`<div>${formatDice(results)}</div>`+
(content.length ? `<div style="${s(styles.content)}">${content}</div>` : '') +
`</div>`;
switch(match[2]){
case 'b':
sendChat(msg.who, `/w gm `+ output );
sendChat('',`/w "${who}" `+
`<div style="${s(styles.body)}">`+
`<div style="${s(styles.result)}">`+
`Blind to GM`+
`</div>`+
(content.length ? `<div style="${s(styles.content)}">${content}</div>` : '') +
`</div>`
);
break;
case 'w':
sendChat(msg.who, `/w gm `+ output );
sendChat('', `/w "${who}" `+ output );
break;
default:
sendChat(msg.who, output );
}
} else {
sendChat('',`/w "${who}" <div>`+
`<div>Use <code>!sly [Difficulty Number] [Inline Roll] [Optional Content]</code></div>`+
`<div>Use <code>!wsly</code> to have output whispered to player and gm. (secret roll)</div>`+
`<div>Use <code>!bsly</code> to have output whispered to gm. (blind roll)</div>`
);
}
}
}
});
});