Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

I'm rolling 3d20. The middle one is a target result. Each 20s on any of the dice adds 3 to the result. Each 1s on any of the dice subtracts 3 from the result. The final result may be negative.

Is that even possible. First part is easy, formula 3d20kl2dl1s is doing job well. But how can I modify the result after dice dropping.
1594744834
Kavini
Pro
Marketplace Creator
Sheet Author
Compendium Curator
Unfortunately, it isn't possible to edit the result of the roll in this way, unless you were to program an API script that does so.
1594749215

Edited 1595277697
David M.
Pro
API Scripter
Here is a simple one-off script that I think does what you want. Nothing fancy, didn't even bother getting the calling player name, formatting numbers, or anything for the chat. You can replace the string "MacroName" in the sendChat line to whatever you want. Just type !3d20 into the chat to call it. Here is sample output: 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. However, the code below is obselete anyway thanks to the updated version from GiGs in a later post. on("ready",function() { on("chat:message",function(msg){ if(msg.type=="api" && msg.content.indexOf("!3d20")==0) { var r = [Math.floor(Math.random() * 20) + 1, Math.floor(Math.random() * 20) + 1, Math.floor(Math.random() * 20) + 1]; var mods = [0,0,0]; let totalMod = 0; let finalResult = 0; r.sort((a,b)=>a-b); for (i = 0; i <= 2; i+=1) { switch(r[i]) { case 1: mods[i] = -3 break; case 20: mods[i] = 3 break; } } totalMod = mods[0] + mods[1] + mods[2]; finalResult = r[1] + totalMod; sendChat('API', '&{template:default\} {{name=MacroName\}\} {{Rolls=[['+r[0].toString()+']]\, [['+r[1].toString()+']]\, [['+r[2].toString()+']]\}\} {{mods=[['+mods[0].toString()+']]\, [['+mods[1].toString()+']]\, [['+mods[2].toString()+']] =[['+totalMod.toString()+']]\}\} {{Total Result=[['+finalResult+']]\}\}') } }); });
1594773232

Edited 1594773349
Oosh
Sheet Author
API Scripter
Nice, David! Might just need a tiny tweak if I'm reading this rightly. I'm probably not though. OP - Just to clarify, is it the middle roll in order of dice rolled (ie the second die), or the middle roll numerically? It looks from your macro like you mean the middle roll numerically, since you were dropping the highest and lowest roll. So a roll of: 10 - 3 - 20 Would the target be 6, or would the target be 13?
1594776206
GiGs
Pro
Sheet Author
API Scripter
Nice work, David. You might be pleased to know there's a randomInteger function available in roll20 scripting, so you can do this&nbsp; var r = [randomInteger(20), randomInteger(20), randomInteger(20)]; See here:&nbsp; <a href="https://wiki.roll20.net/API:Utility_Functions#Random_Numbers" rel="nofollow">https://wiki.roll20.net/API:Utility_Functions#Random_Numbers</a>
1594813332
David M.
Pro
API Scripter
Oosh, the script assumes that the "middle" roll means the 2nd highest number after sorting. So the output of the script would be 13. Easy enough to change if I misinterpreted the OP's problem.&nbsp;
1594813988

Edited 1595277725
David M.
Pro
API Scripter
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)=&gt;a-b)". Original was sorting alphabetically. on('ready',function() { &nbsp;&nbsp;&nbsp;&nbsp;on('chat:message',function(msg){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(msg.type=='api'&nbsp;&amp;&amp;&nbsp;msg.content.indexOf('!3d20')==0) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;MacroName&nbsp;=&nbsp;'3d20'; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;r&nbsp;=&nbsp;[randomInteger(20),&nbsp;randomInteger(20),&nbsp;randomInteger(20)].sort((a,b)=&gt;a-b); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;mods&nbsp;=&nbsp;[]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;modValue&nbsp;=&nbsp;roll&nbsp;=&gt;&nbsp;roll&nbsp;===&nbsp;1&nbsp;?&nbsp;-3&nbsp;:&nbsp;(roll&nbsp;===20&nbsp;?&nbsp;3&nbsp;:&nbsp;0); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;r.forEach((roll,&nbsp;index)&nbsp;=&gt;&nbsp;mods[index]&nbsp;=&nbsp;modValue(roll)); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;totalMod&nbsp;=&nbsp;mods.reduce((sum,&nbsp;n)&nbsp;=&gt;&nbsp;sum&nbsp;+&nbsp;n,&nbsp;0); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;finalResult&nbsp;=&nbsp;r[1]&nbsp;+&nbsp;totalMod; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;displayRoll&nbsp;=&nbsp;arr&nbsp;=&gt;&nbsp;arr.map(roll&nbsp;=&gt;&nbsp;`[[${roll}]]`).join('&nbsp;'); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;output&nbsp;=&nbsp;`&amp;{template:default}&nbsp;{{name=${MacroName}}}&nbsp;{{Rolls=${displayRoll(r)}}}&nbsp;{{Mods=${displayRoll(mods)}}}&nbsp;{{Total&nbsp;Result=${displayRoll([finalResult])}}}`; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendChat('API',&nbsp;output); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;}); }); 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:&nbsp; on ( 'ready' , function () { &nbsp;&nbsp;&nbsp;&nbsp; on ( 'chat:message' , function ( msg ){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( msg . type == 'api' &nbsp;&amp;&amp;&nbsp; msg . content . indexOf ( '!3d20' )== 0 ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;better&nbsp;to&nbsp;put&nbsp;constants&nbsp;at&nbsp;the&nbsp;start,&nbsp;to&nbsp;make&nbsp;it&nbsp;easier&nbsp;to&nbsp;find&nbsp;and&nbsp;edit&nbsp;them&nbsp;later. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const &nbsp; MacroName &nbsp;=&nbsp; '3d20' ; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const &nbsp; r &nbsp;=&nbsp;[ randomInteger ( 20 ),&nbsp; randomInteger ( 20 ),&nbsp; randomInteger ( 20 )]. sort ((a,b)=&gt;a-b)); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const &nbsp; mods &nbsp;=&nbsp;[]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;modValue&nbsp;is&nbsp;a&nbsp;reusable&nbsp;function,&nbsp;that&nbsp;returns&nbsp;-3,&nbsp;0,&nbsp;or&nbsp;3&nbsp;depending&nbsp;on&nbsp;the&nbsp;input&nbsp;value &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;I'm&nbsp;using&nbsp;arrow&nbsp;syntax&nbsp;here:&nbsp;functionName&nbsp;= (parameters)&nbsp;=&gt;&nbsp;{what&nbsp;the&nbsp;function&nbsp;does}; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;with&nbsp;single&nbsp;line&nbsp;functions,&nbsp;you&nbsp;can&nbsp;dispense&nbsp;with&nbsp;the&nbsp;{&nbsp;}&nbsp;block,&nbsp;and&nbsp;return&nbsp;statement,&nbsp;making&nbsp;them&nbsp;very&nbsp;compact. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const &nbsp; modValue &nbsp;=&nbsp; roll &nbsp; =&gt; &nbsp; roll &nbsp;===&nbsp; 1 &nbsp;?&nbsp;- 3 &nbsp;:&nbsp;( roll &nbsp;=== 20 &nbsp;?&nbsp; 3 &nbsp;:&nbsp; 0 ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;forEach&nbsp;loops&nbsp;through&nbsp;an&nbsp;array.&nbsp;index&nbsp;is&nbsp;an&nbsp;optional&nbsp;parameter&nbsp;that&nbsp;tells&nbsp;you&nbsp;what&nbsp;position&nbsp;in&nbsp;the&nbsp;array&nbsp;it&nbsp;is. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r . forEach (( roll ,&nbsp; index )&nbsp; =&gt; &nbsp; mods [ index ]&nbsp;=&nbsp; modValue ( roll )); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;reduce&nbsp;is&nbsp;a&nbsp;very&nbsp;complex&nbsp;function,&nbsp;but&nbsp;here&nbsp;is&nbsp;simple&nbsp;and&nbsp;just&nbsp;adds&nbsp;up&nbsp;all&nbsp;items&nbsp;in&nbsp;an&nbsp;array. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // &nbsp; Sum is the running total, with n being the next item in the array, and 0 the initial value. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const &nbsp; totalMod &nbsp;=&nbsp; mods . reduce (( sum ,&nbsp; n )&nbsp; =&gt; &nbsp; sum &nbsp;+&nbsp; n ,&nbsp; 0 ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const &nbsp; finalResult &nbsp;=&nbsp; r [ 1 ]&nbsp;+&nbsp; totalMod ; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;since&nbsp;every&nbsp;template&nbsp;value&nbsp;is&nbsp;the&nbsp;same&nbsp;(numbers&nbsp;surrounded&nbsp;by&nbsp;[[&nbsp;]]),&nbsp;we&nbsp;can&nbsp;create&nbsp;a&nbsp;function&nbsp;to&nbsp;create&nbsp;them. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;map&nbsp;is&nbsp;like&nbsp;a&nbsp;loop:&nbsp;it&nbsp;does&nbsp;something&nbsp;to&nbsp;every&nbsp;item&nbsp;in&nbsp;the&nbsp;array,&nbsp;and&nbsp;creates&nbsp;a&nbsp;new&nbsp;array&nbsp;as&nbsp;the&nbsp;result. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;join&nbsp;then&nbsp;joins&nbsp;an&nbsp;array&nbsp;into&nbsp;a&nbsp;string,&nbsp;with&nbsp;the&nbsp;bracketed&nbsp;item&nbsp;being&nbsp;inserted&nbsp;as&nbsp;a&nbsp;separator. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;not&nbsp;the&nbsp;use&nbsp;of&nbsp;template&nbsp;literal&nbsp;for&nbsp;the&nbsp;string&nbsp;part&nbsp;-&nbsp;more&nbsp;on&nbsp;that&nbsp;below. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const &nbsp; displayRoll &nbsp;=&nbsp; arr &nbsp; =&gt; &nbsp; arr . map ( roll &nbsp; =&gt; &nbsp; `[[ ${ roll } ]]` ). join ( '&nbsp;' ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;we&nbsp;build&nbsp;the&nbsp;output&nbsp;string&nbsp;with&nbsp;a&nbsp;template&nbsp;literal.&nbsp;This&nbsp;lets&nbsp;you&nbsp;use&nbsp;just&nbsp;one&nbsp;string,&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // no&nbsp;need&nbsp;for&nbsp;escape&nbsp;characters,&nbsp;and&nbsp;you&nbsp;can&nbsp;insert&nbsp;variables&nbsp;and&nbsp;code&nbsp;inside&nbsp;${&nbsp;}&nbsp;blocks. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const &nbsp; output &nbsp;=&nbsp; `&amp;{template:default}&nbsp;{{name= ${ MacroName } }}&nbsp;{{Rolls= ${ displayRoll ( r ) } }}&nbsp;{{Mods= ${ displayRoll ( mods ) } }}&nbsp;{{Total&nbsp;Result= ${ displayRoll ([ finalResult ]) } }}` ; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;The&nbsp;above&nbsp;could&nbsp;have&nbsp;been&nbsp;inserted&nbsp;directly&nbsp;into&nbsp;sendChat&nbsp;instead&nbsp;of&nbsp;using&nbsp;the&nbsp;output&nbsp;variable,&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // but&nbsp;you&nbsp;might&nbsp;want&nbsp;to&nbsp;log&nbsp;it&nbsp;and&nbsp;check&nbsp;it's&nbsp;okay. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sendChat ( 'API' ,&nbsp; output ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;}); }); Love this community!&nbsp;
Thanks David, the script works fine. But there is one small mistake. On line 6, where the throws results are sorted.&nbsp; It should be .sort (function (a, b) {return b-a}). Without this, the sort method does not work properly and the middle result is not correct some times.
1595260820

Edited 1595261335
GiGs
Pro
Sheet Author
API Scripter
Oh yes, good catch. By default it sorts alphabetically, character by character, so 11 is considered 'smaller' than 9. I've been sorting strings so much lately I forgot about that.
1595277473
David M.
Pro
API Scripter
Thanks, Azbest! I had no idea, seeing as I just started with js a couple weeks ago, haha. Yet another nuance to learn / keep track of :) I'll edit the above with the correction.
Is there any way to debug script online. I use to use F12 debug menu but I cannot find my script in source, so I cannot put brakingpoint/traps in it.&nbsp; Usage of debugger comand in code doesn't work either;
1595318778
GiGs
Pro
Sheet Author
API Scripter
There's no good way to debug scripts within roll20 unfortunately. Just liberal use of sendChat and log for logging. native javascript does provide try...catch blocks which you can use to isolate problem areas of code and examine them. But you have to manually create them within the code.