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

Roll vs A Shifting Table

New poster needing help!  My GM is running a game and we need to make it easier for everyone.  We use a shifting table to determine results.  I'll include an example of the table below.  The long and short is we would like to be help the players make this easier.  As it stands, even if we know what column we are rolling on, on the table, we have to spend a minute or two determining the results.  There are 6 possible results from the percentile roll, and they are not evenly distributed and not really based on a character attributes. So, what macro could we use to make this happen?  I would like to know how to tell if a 72 on the -4 column is a green hit or a yellow hit without spending five minutes looking for it.  Can we have the players choose the CS and roll?  How do we handle this issue?  Here's an example of the table:
With five comparisons to make the doing the roll and the comparisons at the same time is problematic at best. Doing a lookup after the roll is a simpler task If I'm reading this correctly a hit occurs if Roll &lt;= Target, which we can rearrange to be Target - Roll &gt;= 0 This lets us use grouped target number to count hits for multiple targets. Making the +9CS roll to be [[{1 -?{Roll}, 6 -?{Roll}, 31 -?{Roll}, 56 -?{Roll}, 81 -?{Roll}}&gt;0]] This will return a number 0 to 5 Would be off table Red hit Orange hit Yellow hit Green hit Blue hit Using that as a base you can create a roll for each CS value. Then either use separate macros or put them into a Roll Query to select the CS. If you do put them into a single Roll Query you'll need to replace the } and commas with their HTML entities. For details see&nbsp; <a href="https://roll20.zendesk.com/hc/en-us/articles/360037256794-Macros#Macros-AdvancedUsageforRollQueries" rel="nofollow">https://roll20.zendesk.com/hc/en-us/articles/360037256794-Macros#Macros-AdvancedUsageforRollQueries</a>
1605506014
Oosh
Sheet Author
API Scripter
Is your GM also on a Pro membership? This looks like like fertile ground for one of David's scripts....
It does look like he has a Pro membership.&nbsp; And thanks for the help so far.&nbsp; I'm still working on understanding it all, but every little bit helps!
1605538752

Edited 1605540944
David M.
Pro
API Scripter
Looks like your DM posted a similar thread and mentioned it was 6 success levels (fumble, miss, partial success, success, enhanced success, critical success). Put a quick script together, assuming high rolls were good. So for the Shift 0 row, assumption was: 96-100 = crit success 89-95 = enhanced success 76-88 = success 46-75 = partial success 05-45 = miss 01-04 = fumble If that is not the case, should be easy for you to modify appropriately. Didn't do extensive testing, so may include typos in the building of the table arrays. Sample output Syntax !CS &lt;#&gt; e.g. !CS -7&nbsp;&nbsp;&nbsp;&nbsp;//roll d100 on the -7CS table !CS 6&nbsp;&nbsp;&nbsp;&nbsp;//roll d100 on the +6CS table The script const CS = (() =&gt; { const version = '0.1.0'; //Arrays to simulate tables with success limits let CS9 = [1, 6, 31, 56, 81]; let CS8 = [1, 9, 36, 61, 86]; let CS7 = [3, 11, 41, 66, 86]; let CS6 = [3, 16, 46, 71, 89]; let CS5 = [3, 21, 51, 71, 89]; let CS4 = [3, 26, 56, 76, 92]; let CS3 = [5, 31, 61, 81, 92]; let CS2 = [5, 36, 66, 86, 94]; let CS1 = [5, 41, 71, 86, 94]; let CS0 = [5, 46, 76, 89, 96]; let CS_1 = [8, 51, 76, 89, 96]; let CS_2 = [8, 56, 81, 92, 96]; let CS_3 = [10, 61, 81, 92, 96]; let CS_4 = [10, 66, 86, 94, 98]; let CS_5 = [15, 71, 89, 94, 98]; let CS_6 = [15, 76, 92, 96, 98]; let CS_7 = [20, 81, 92, 96, 98]; let CS_8 = [20, 86, 94, 98, 100]; let CS_9 = [25, 89, 96, 98, 100]; const checkInstall = () =&gt; { log('-=&gt; CS v'+version); }; //sendChat output formatting styles, only one style right now const _h = { inlineResult: (...o) =&gt; `&lt;span style="font-weight: bold;padding: .2em .2em; background-color:rgba(254,246,142,1);"&gt;${o.join('')}&lt;/span&gt;` }; const getResult = function (tableArr, roll) { if (roll&gt;=tableArr[4]) {return "Critical Success"} if (roll&gt;=tableArr[3] &amp;&amp; roll&lt;tableArr[4]) {return "Enhanced Success"} if (roll&gt;=tableArr[2] &amp;&amp; roll&lt;tableArr[3]) {return "Success"} if (roll&gt;=tableArr[1] &amp;&amp; roll&lt;tableArr[2]) {return "Partial Success"} if (roll&gt;=tableArr[0] &amp;&amp; roll&lt;tableArr[1]) {return "Miss"} if (roll&lt;tableArr[0]) {return "Fumble"}; } const handleInput = (msg) =&gt; { const scriptName = 'CS'; let roll = 0; let tableName; let tableArr; let result; if(msg.type=="api" &amp;&amp; msg.content.indexOf("!CS") === 0 ) { try { who = getObj('player',msg.playerid).get('_displayname'); let args = msg.content.split(/\s+/); if (args[1]&gt;=0) { tableName = 'CS' + parseInt(args[1]); } else { tableName = 'CS_' + -1*parseInt(args[1]); } switch(tableName){ case "CS9": tableArr = CS9; break; case "CS8": tableArr = CS8; break; case "CS7": tableArr = CS7; break; case "CS6": tableArr = CS6; break; case "CS5": tableArr = CS5; break; case "CS4": tableArr = CS4; break; case "C3": tableArr = CS3; break; case "CS2": tableArr = CS2; break; case "CS1": tableArr = CS1; break; case "CS0": tableArr = CS0; break; case "CS_1": tableArr = CS_1; break; case "CS_2": tableArr = CS_2; break; case "CS_3": tableArr = CS_3; break; case "CS_4": tableArr = CS_4; break; case "CS_5": tableArr = CS_5; break; case "CS_6": tableArr = CS_6; break; case "CS_7": tableArr = CS_7; break; case "CS_8": tableArr = CS_8; break; case "CS_9": tableArr = CS_9; break; } tableName = tableName.replace("_", "-"); roll = randomInteger(100); result = getResult(tableArr, roll); let output = '&amp;{template:default} {{name=' + tableName + '}} {{Roll=[[' + roll + ']]}} {{Result=' + _h.inlineResult(result) + '}}'; sendChat(scriptName, output); } catch(err) { sendChat(scriptName,`/w "${who}" `+ 'Error: ' + err.message); } }; }; const registerEventHandlers = () =&gt; { on('chat:message', handleInput); }; on('ready', () =&gt; { checkInstall(); registerEventHandlers(); }); })();
GM here, David I am going to focus here instead of the other thread and I'll let Damn Eggo II take the reins on this one as he is the script-master for our game :) Thank you so much for your reply :)
This is the full version of the table, as there is the White result which is just a miss. Hope this helps :)
1605547741

Edited 1605547997
GiGs
Pro
Sheet Author
API Scripter
What game system is that for? Some small questions that need answers before anyone can create a solution for it: Also how do you use that Intensity table at the top? And how do you use the table at the bottom with the blunt attacks, edged attacks, etc. And finally, how do you use the table in the middle? How are CS values set?
1605549897

Edited 1605550090
David M.
Pro
API Scripter
Bear, your image looks like a broken link for me?
I was going to post the image, again, but it is the same as he posted.&nbsp; So, here is the linked: Here is link
1605551651
GiGs
Pro
Sheet Author
API Scripter
There are three tables there, the picture is useless without an explanation of how to use them, and what sort of help you want with them.
1605554203
David M.
Pro
API Scripter
Lol, yep. I sense a little scope creep :) At least what I threw together above didn't take very long (data entry was the most tedious part - rest just based on another script that had 80% of it done already). Was just a glimpse into one approach to the lookups you could take. Also looks like the comparisons (&gt;=, &amp;&amp; &lt;) to the limit values are a bit off, now that I see the full chart. Looks like you take the success level and compare to the attack type table (bottom one), with the bottom one being the ultimate output. That top table is a mystery, though. Also, there are probably mods to the d100 roll?&nbsp;
1605566459
Oosh
Sheet Author
API Scripter
Does the designer of this game just have a deep hatred of players?
No mods on the rolls.&nbsp; Btw, any way to see the dice rolls, with the table?
So this is for my neo-clone of the old Marvel Super Heroes RPG by TSR (affectionally called FASERIP). The middle table is all that matters for the API. Basically the player should be able to select which column they are rolling on (-9 to +9) and then generate a d100 roll and have the colour result. The top table are the "Ranks", which are used to represent their power or ability level (like the classic 3 - 18 in D&amp;D) and the bottom one are the Results based on the type of action they took. These are both outside of the scope of what is needed for this API. Much love David, you are the MAN for what you've done here sir. I bow to your ability. Oh and Oosh, guess I should expect you for a playtest then eh? ;) :) :D BE HEROIC!
1605590345
David M.
Pro
API Scripter
Glad it was helpful, though I must admit I'm pretty much just a hack. You should browse the forums and see some of the wizardry that GiGs and Oosh pull out of their hats! Re: 3D dice - I'm pretty sure that 3D dice can't be triggered from the API. One of the gurus could probably confirm or refute. Thought it would be a fun exercise, so in case it's useful here's another version, with the fumble numbers adjusted by one to make the logic work out for the edge cases (I think). New input now includes the CS and the type of action, and returns the d100 roll and color coded Result specific to the action, rather than just the generic success level. Could make the ActionType optional if it is ever useful to just get success levels like the previous version. ActionType is currently required. !CS &lt;#&gt; &lt;ActionType&gt; e.g. !CS -3 Force !CS +5 RollWithTheBlow Here's a query macro to handle all cases and prevent typos in the ActionType input. Note no spaces, as I was too lazy to change my parsing :) !CS ?{select CS|9|8|7|6|5|4|3|2|1|0|-1|-2|-3|-4|-5|-6|-7|-8|-9} ?{select Action|BluntAttack|EdgedAttack|Shooting|ThrowingEdged|ThrowingBlunt|Energy|Force|Grappling|Grabbing|Escaping|Charging|Dodging|Evading|Blocking|Catching|Stun|Slam|KO|Bleed|Kill|RollWithTheBlow|BounceBack} Sample output Code const CS = (() =&gt; { const version = '0.1.0'; const successText = ["Fumble", "Miss", "Partial Success", "Success", "Enhanced Success", "Critical Success"];&nbsp;&nbsp;&nbsp;&nbsp;//not used let CS9 = [2, 6, 31, 56, 81]; let CS8 = [2, 9, 36, 61, 86]; let CS7 = [4, 11, 41, 66, 86]; let CS6 = [4, 16, 46, 71, 89]; let CS5 = [4, 21, 51, 71, 89]; let CS4 = [4, 26, 56, 76, 92]; let CS3 = [6, 31, 61, 81, 92]; let CS2 = [6, 36, 66, 86, 94]; let CS1 = [6, 41, 71, 86, 94]; let CS0 = [6, 46, 76, 89, 96]; let CS_1 = [9, 51, 76, 89, 96]; let CS_2 = [9, 56, 81, 92, 96]; let CS_3 = [11, 61, 81, 92, 96]; let CS_4 = [11, 66, 86, 94, 98]; let CS_5 = [16, 71, 89, 94, 98]; let CS_6 = [16, 76, 92, 96, 98]; let CS_7 = [21, 81, 92, 96, 98]; let CS_8 = [21, 86, 94, 98, 100]; let CS_9 = [26, 89, 96, 98, 100]; let bluntAttack = ["Fumble", "Miss", "Hit", "Slam", "Stun", "K.O."]; let edgedAttack = ["Fumble", "Miss", "Hit", "Slam", "Bleed", "Kill"]; let shooting = ["Fumble", "Miss", "Hit", "Bullseye", "Bleed", "Kill"]; let throwingEdged = ["Fumble", "Miss", "Hit", "Bullseye", "Bleed", "Kill"]; let throwingBlunt = ["Fumble", "Miss", "Hit", "Bullseye", "Stun", "K.O."]; let energy = ["Fumble", "Miss", "Hit", "Bullseye", "Stun", "Kill"]; let force = ["Fumble", "Miss", "Hit", "Bullseye", "Stun", "Slam"]; let grappling = ["Fumble", "Miss", "Hit", "Partial", "Hold", "Stun"]; let grabbing = ["Fumble", "Miss", "Take", "Grab", "Grab", "Break"]; let escaping = ["Fumble", "Miss", "Miss", "Escape", "Reverse", "Counter"]; let charging = ["Fumble", "Miss", "Hit", "Slam", "Stun", "K.O."]; let dodging = ["Autohit", "None", "-1CS", "-2CS", "-4CS", "-8CS"]; let evading = ["+Stun", "Autohit", "Evasion", "+1CS", "+2CS", "+3CS"]; let blocking = ["+Stun", "-6CS", "-4CS", "-2CS", "-1CS", "+1CS"]; let catching = ["+Stun", "Autohit", "Miss", "Damage", "Catch", "Riposte"]; let stun = ["+K.O.", "1-10", "1", "No", "No", "No"]; let slam = ["+Stun", "Gr. Slam", "1 Area", "Stagger", "No", "Deflect"]; let ko = ["+Kill", "Knockout", "+Stun", "No", "No", "No"]; let bleed = ["+Kill", "Bleed", "-1", "No", "No", "No"]; let kill = ["KIA", "En. Loss", "E/S", "No", "No", "No"]; let rollWithTheBlow = ["+1CS", "No", "-1CS", "-2CS", "-3CS", "-4CS"]; let bounceBack = ["+Stun", "No", "-1CS", "End", "+1CS", "+2CS"]; const checkInstall = () =&gt; { log('-=&gt; CS v'+version); }; //sendChat output formatting styles const _h = { fumble: (...o) =&gt; `&lt;span style="font-weight: bold;padding: .2em .2em; background-color:rgba(1,176,241,1);"&gt;${o.join('')}&lt;/span&gt;`, miss: (...o) =&gt; `&lt;span style="font-weight: bold;padding: .2em .2em; background-color:rgba(255,255,255,1);"&gt;${o.join('')}&lt;/span&gt;`, partial: (...o) =&gt; `&lt;span style="font-weight: bold;padding: .2em .2em; background-color:rgba(1,144,2,1);"&gt;${o.join('')}&lt;/span&gt;`, success: (...o) =&gt; `&lt;span style="font-weight: bold;padding: .2em .2em; background-color:rgba(254,246,142,1);"&gt;${o.join('')}&lt;/span&gt;`, enhanced: (...o) =&gt; `&lt;span style="font-weight: bold;padding: .2em .2em; background-color:rgba(191,116,1,1);"&gt;${o.join('')}&lt;/span&gt;`, crit: (...o) =&gt; `&lt;span style="font-weight: bold;padding: .2em .2em; background-color:rgba(192,0,0,1);"&gt;${o.join('')}&lt;/span&gt;` }; const formattedResult = function (successLevel, text) { switch(successLevel){ case 0: return _h.fumble(text); break; case 1: return _h.miss(text); break; case 2: return _h.partial(text); break; case 3: return _h.success(text); break; case 4: return _h.enhanced(text); break; case 5: return _h.crit(text); break; } } const getSuccessLevel = function (tableArr, roll) { if (roll&gt;=tableArr[4]) {return 5} //"Critical Success" if (roll&gt;=tableArr[3] &amp;&amp; roll&lt;tableArr[4]) {return 4} //"Enhanced Success" if (roll&gt;=tableArr[2] &amp;&amp; roll&lt;tableArr[3]) {return 3} //"Success" if (roll&gt;=tableArr[1] &amp;&amp; roll&lt;tableArr[2]) {return 2} //"Partial Success" if (roll&gt;=tableArr[0] &amp;&amp; roll&lt;tableArr[1]) {return 1} //"Miss" if (roll&lt;tableArr[0]) {return 0} //"Fumble" }; const handleInput = (msg) =&gt; { const scriptName = 'CS'; let roll = 0; let tableName; let tableArr; let actionArr; let action = ""; let successLevel; let result; if(msg.type=="api" &amp;&amp; msg.content.indexOf("!CS") === 0 ) { try { who = getObj('player',msg.playerid).get('_displayname'); let args = msg.content.split(/\s+/); if (args[1]&gt;=0) { tableName = 'CS' + parseInt(args[1]); } else { tableName = 'CS_' + -1*parseInt(args[1]); } //Get Success Level Array switch(tableName){ case "CS9": tableArr = CS9; break; case "CS8": tableArr = CS8; break; case "CS7": tableArr = CS7; break; case "CS6": tableArr = CS6; break; case "CS5": tableArr = CS5; break; case "CS4": tableArr = CS4; break; case "C3": tableArr = CS3; break; case "CS2": tableArr = CS2; break; case "CS1": tableArr = CS1; break; case "CS0": tableArr = CS0; break; case "CS_1": tableArr = CS_1; break; case "CS_2": tableArr = CS_2; break; case "CS_3": tableArr = CS_3; break; case "CS_4": tableArr = CS_4; break; case "CS_5": tableArr = CS_5; break; case "CS_6": tableArr = CS_6; break; case "CS_7": tableArr = CS_7; break; case "CS_8": tableArr = CS_8; break; case "CS_9": tableArr = CS_9; break; } //Get Action Array action = args[2]; switch(args[2]){ case "BluntAttack": actionArr = bluntAttack; break; case "EdgedAttack": actionArr = edgedAttack; break; case "Shooting": actionArr = shooting; break; case "ThrowingEdged": actionArr = throwingEdged; break; case "ThrowingBlunt": actionArr = throwingBlunt; break; case "Energy": actionArr = energy; break; case "Force": actionArr = force; break; case "Grappling": actionArr = grappling; break; case "Grabbing": actionArr = grabbing; break; case "Escaping": actionArr = escaping; break; case "Charging": actionArr = charging; break; case "Dodging": actionArr = dodging; break; case "Evading": actionArr = evading; break; case "Blocking": actionArr = blocking; break; case "Catching": actionArr = catching; break; case "Stun": actionArr = stun; break; case "Slam": actionArr = slam; break; case "KO": actionArr = ko; break; case "Bleed": actionArr = bleed; break; case "Kill": actionArr = kill; break; case "RollWithTheBlow": actionArr = rollWithTheBlow; break; case "BounceBack": actionArr = bounceBack; break; default: sendChat(scriptName,`/w "${who}" `+ 'Error: Action \"' + action + '\" not found.'); break } tableName = tableName.replace("_", "-"); roll = randomInteger(100); successLevel = getSuccessLevel(tableArr, roll); result = actionArr[successLevel]; //let output = '&amp;{template:default} {{name=' + tableName + '}} {{Roll=[[' + roll + ']]}} {{SuccessLevel=' + _h.inlineResult(result) + '}}'; let output = '&amp;{template:default} {{name=' + action + ' ' + tableName + '}} {{Roll=[[' + roll + ']]}} {{Result=' + formattedResult(successLevel, result) + '}}'; sendChat(scriptName, output); } catch(err) { sendChat(scriptName,`/w "${who}" `+ 'Error: ' + err.message); } }; }; const registerEventHandlers = () =&gt; { on('chat:message', handleInput); }; on('ready', () =&gt; { checkInstall(); registerEventHandlers(); }); })(); Had a fun time with this. Let me know if there is a problem somewhere.
Wonderfully done!&nbsp; It is a great help for the game.&nbsp; I'm going to be getting a lot of use from it.&nbsp; lol&nbsp; Thank you, kindly!