Also, GiGs was very kind to help me out with a more javascript-y version of the above script that could fit on the head of a pin! Here it is for those interested: EDIT -- Thanks to Azbest, I corrected the .sort call below in order to perform a numeric sort, via ".sort((a,b)=>a-b)". Original was sorting alphabetically. on('ready',function() { on('chat:message',function(msg){ if(msg.type=='api' && msg.content.indexOf('!3d20')==0) { const MacroName = '3d20'; const r = [randomInteger(20), randomInteger(20), randomInteger(20)].sort((a,b)=>a-b); const mods = []; const modValue = roll => roll === 1 ? -3 : (roll ===20 ? 3 : 0); r.forEach((roll, index) => mods[index] = modValue(roll)); const totalMod = mods.reduce((sum, n) => sum + n, 0); const finalResult = r[1] + totalMod; const displayRoll = arr => arr.map(roll => `[[${roll}]]`).join(' '); const output = `&{template:default} {{name=${MacroName}}} {{Rolls=${displayRoll(r)}}} {{Mods=${displayRoll(mods)}}} {{Total Result=${displayRoll([finalResult])}}}`; sendChat('API', output); } }); }); Finally, if anybody is new to js like I am, or thinking about getting into it, then the fully commented version of the code [below] is extremely educational: on ( 'ready' , function () { on ( 'chat:message' , function ( msg ){ if ( msg . type == 'api' && msg . content . indexOf ( '!3d20' )== 0 ) { // better to put constants at the start, to make it easier to find and edit them later. const MacroName = '3d20' ; const r = [ randomInteger ( 20 ), randomInteger ( 20 ), randomInteger ( 20 )]. sort ((a,b)=>a-b)); const mods = []; // modValue is a reusable function, that returns -3, 0, or 3 depending on the input value // I'm using arrow syntax here: functionName = (parameters) => {what the function does}; // with single line functions, you can dispense with the { } block, and return statement, making them very compact. const modValue = roll => roll === 1 ? - 3 : ( roll === 20 ? 3 : 0 ); // forEach loops through an array. index is an optional parameter that tells you what position in the array it is. r . forEach (( roll , index ) => mods [ index ] = modValue ( roll )); // reduce is a very complex function, but here is simple and just adds up all items in an array.
// Sum is the running total, with n being the next item in the array, and 0 the initial value. const totalMod = mods . reduce (( sum , n ) => sum + n , 0 ); const finalResult = r [ 1 ] + totalMod ; // since every template value is the same (numbers surrounded by [[ ]]), we can create a function to create them. // map is like a loop: it does something to every item in the array, and creates a new array as the result. // join then joins an array into a string, with the bracketed item being inserted as a separator. // not the use of template literal for the string part - more on that below. const displayRoll = arr => arr . map ( roll => `[[ ${ roll } ]]` ). join ( ' ' ); // we build the output string with a template literal. This lets you use just one string,
// no need for escape characters, and you can insert variables and code inside ${ } blocks. const output = `&{template:default} {{name= ${ MacroName } }} {{Rolls= ${ displayRoll ( r ) } }} {{Mods= ${ displayRoll ( mods ) } }} {{Total Result= ${ displayRoll ([ finalResult ]) } }}` ; // The above could have been inserted directly into sendChat instead of using the output variable,
// but you might want to log it and check it's okay. sendChat ( 'API' , output ); } }); }); Love this community!