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

Getting input from a player for an API script and calling a macro from a script

April 17 (10 years ago)
Ok, so I intend to have a macro that I (DM) will write and make available to all players. This macro will ask them for their damage roll, and then (This is the important bit) how many times to roll it.

I am ready to start writing API (because you can't do the above in a macro, I've checked and asked) and realised I don't know how to get input from the players (such as the damage roll) into the API script.
Ideally the script would:

1. Trigger when anyone hits the "Damage-Roll" macro
2. Store the damage they wanna roll
3. Store how many times it should be rolled (Don't ask, a mate is inventing a system, kind of)
4. Actually roll it that many times, which as far as I am aware should be as simple as:

MakeDamageRollPretty( damageRoll );
for( int i = 0; i < numberOfTimesToRoll; i++ )
{
DelayByXSecondsForSuspense( X );
PutThisStringInChatAndExecuteIt ( damageRollMadePretty );
}

The problem I have is not knowing how to do a bunch of things: Get numberOfTimesToRoll, damageRoll from the player at 'runtime', make it trigger when they click the macro, and... Well tbh, any of it. I'm new to javascript, my background pretty much just C# and some C++ over 3 years ago.

'Elp?

Thanks in advance :)

P.S. rereading that it seems I am basically asking you to write it for me... the problem is I have no idea where to start so advice on where to start would also be handy :)
April 18 (10 years ago)
Lithl
Pro
Sheet Author
API Scripter

The basics of creating an API command is the chat:message message event. You'll have a callback function with one parameter, which will be an object storing all the info you need. the 'content' property stores the original text of the message.

on('chat:message', function(msg) {
if (msg.type !== 'api') return; // type will be 'api' if the user began the message with "!", making it an API command

var parts = msg.content.split(' '); // msg.content is the text of the message
var command = parts.shift().substring(1); // shift() removes the first element of the array and returns it

if (command.toLowerCase() === 'repeatroll' && parts.length > 1) {
var roll = parts.slice(0, parts.length - 1); // slice() is like substring() for arrays
var repeat = parseInt(parts.pop()); // pop() removes the last element of the array and returns it
if (!repeat) return; // if the parseInt() returned NaN, quit

var output = '';
for(var i = 0; i < repeat; i++) {
output += '[[' + roll + ']], ';
}
output = output.substring(0, output.length - 2);

sendChat('player|' + msg.playerid, output); // player|-Jacb123 posts a message as the player with the id -Jabc123,
// character|-J123abc posts a as the character with the id -J123abc,
// any other value for the first parameter is like using /as
// playerid is the id of the player that send the API command
}
});

The above is pretty rough and dirty, but it should be a functional solution to what you're looking for. You'd have a macro like !repeatroll 1d20 + 5 10 and it would roll 1d20+5 10 times. With some programming background, you should be able to make sense of the above pretty easily.

If you want something more elegant, HoneyBadger's Power Cards script accomplishes what you're looking for and a whole lot more. It's really quite a good bit of work, although it's long, complicated, and hard to read for someone just jumping into the world of Roll20 API Scripts.

April 18 (10 years ago)
The Aaron
Pro
API Scripter
Also be sure to check out the official Roll20 github repo of scripts: https://github.com/Roll20/roll20-api-scripts
There are examples of varying sizes.

Also, I highly recommend Javascript: The Good Parts by Douglas Crockford. It's choke full of great info!
April 18 (10 years ago)
Sweet!
I am gonna copy that out character by character so I start getting used to the syntax.

Thank you both, I can't wait to start playing around with it :D

And yeah I looked at those honeybadger scripts and it fairly melted my brain so figured I might start with something simpler :p
With this example (and if any devs read this, thats a really good example, put it somewhere obvious for noobs to find) I can make a start.
I'll probably be back with more questions (In new posts most likely, this one is answered). Two of my Roll20 games this weekend are off because life so I got loads of free time.

Again, thanks lads :)
April 18 (10 years ago)
Hey so I am having an issue with how I want to display the results and since it works if I put the rolls into a macro and not when I do it via API I am guessing there is some sort of parsing issue with the API or that i don't know how to make it do what I want.

on('chat:message', function(msg) {
    if (msg.type !== 'api') return; // type will be 'api' if the user began the message with "!", making it an API command


    var parts = msg.content.split(' '); // msg.content is the text of the message
    var command = parts.shift().substring(1); // shift() removes the first element of the array and returns it


    if (command.toLowerCase() === 'rr' && parts.length > 1) {
        var roll = parts.slice(0, parts.length - 1); // slice() is like substring() for arrays
        var repeat = parseInt(parts.pop()); // pop() removes the last element of the array and returns it
        if (!repeat) return; // if the parseInt() returned NaN, quit


        var output = '&{template:default} {{name=Damage Roll}}';
        for(var i = 0; i < repeat; i++) {
            output += " {{[[" + roll + "]] Damage to =[[(1t[Hit-Location])]]}}";
        }
        sendChat('player|' + msg.playerid, output); // player|-Jacb123 posts a message as the player with the id -Jabc123,
                                                    // character|-J123abc posts a as the character with the id -J123abc,
                                                    // any other value for the first parameter is like using /as
                                                    // playerid is the id of the player that send the API command
    }
});
The problem is the line:
output += " {{[[" + roll + "]]=[[(1t[Hit-Location])]]}}";
Unless I put ( ) around '1t[Hit-Location]' then I get an error saying it doesn't know what to do with expr '1' or with text 't[Hit-Location'
... I have no idea why it is separating the 1 from the rest of the table rolling code or why its taken the final ']' off it either.

When I put it in ( ) it rolls but instead of saying "11 Damage to Body" it says "11 Damage to 0".
If I put:
&{template:default} {{name=Damage Roll}} {{ [[2d6]] Damage to=[[1t[Hit-Location]]]}}
in a macro the result is "11 Damage to Body"

Any ideas?

April 18 (10 years ago)
Not sure roll templates work with the API yet.
April 18 (10 years ago)
I have it rolling a template and it looks fine so long as I don't try to roll on table.
I guess that might not be in though. That is actually a problem though, Though I guess it doesn't HAVE to be in a template...
I'll changing it so its not a template.
... *Some Time Later*
Its not the template, for some reason when you roll on a table the result is always 0 so when you put it in ( ) it resolves to 0 because thats the result of the roll. So the problem is that the API can't roll on a table unless it is in ( ) but doing so means you don't see the relevant result.

So the next question is how do I tell people who need to know about stuff like this?

P.S. For a second there I thought I had a solution: Put the table rolling in a macro (so its not in the API and hence the API doesn't have to parse it) called "Location-Roll" then put the call to it into what the API would be throwing into chat. Didn't work. BUT if you put that in a macro it does work.
So: &{template:default} {{name=bla}} {{location=#Location-Roll }} works in a macro where #Location-Roll = "[[1t[Hit-Location]]]"

But putting together that exact line in the API and passing it into chat results in: "location #Location-Roll" instead of "location body"

Long story short: API does handle templates, but not table rolls (not as expected anyway) and not macro calls. but these things DO work outside the API. I tested all this fairly thoroughly but if you know of a way to do some of the things I claim can't be done, let me know.
April 20 (10 years ago)
Got round it by using randomInteger to fake the table in a series of if else statements for weighting.

Not a great solution for large tables but it it works for this as there are only 6 items.

Here is the final code so folk can see how it worked out. If you want to test it, you can copy paste the entire script into your campaign API and it should just work since it doesn't depend on you having anything. Trigger it with !rr damageRoll NumberOfRolls
For Example: !rr 29d7+42 5 will roll 29d7+42 5 times, display each roll in a section of a template and tell you which body part each roll hit.

on('chat:message', function(msg) {
    if (msg.type !== 'api') return; // type will be 'api' if the user began the message with "!", making it an API command


    var parts = msg.content.split(' '); // msg.content is the text of the message
    var command = parts.shift().substring(1); // shift() removes the first element of the array and returns it


    if (command.toLowerCase() === 'rr' && parts.length > 1) {
        var roll = parts.slice(0, parts.length - 1); // slice() is like substring() for arrays
        var repeat = parseInt(parts.pop()); // pop() removes the last element of the array and returns it
        if (!repeat) return; // if the parseInt() returned NaN, quit
        
        var location;
        
        var output = '&{template:default} {{name=Damage Roll}}';
//        var output = "[[" + roll + "]] Damage to [[1t[Hit-Location]]]";
        for(var i = 0; i < repeat; i++) {
            var tableRoll = randomInteger(100);
            if(tableRoll <=50) location = "Body";
            else if(tableRoll <=60 && tableRoll > 50) location = "Head";
            else if(tableRoll <=70 && tableRoll > 60) location = "Left Arm";
            else if(tableRoll <=80 && tableRoll > 70) location = "Right Arm";
            else if(tableRoll <=90 && tableRoll > 80) location = "Left Leg";
            else if(tableRoll <=100 && tableRoll > 90) location = "Right Leg";
            else location = "Error";
            output += " {{[[" + roll + "]] Damage to="+location+"}}";
        }
        sendChat('player|' + msg.playerid, output); // player|-Jacb123 posts a message as the player with the id -Jabc123,
                                                    // character|-J123abc posts a as the character with the id -J123abc,
                                                    // any other value for the first parameter is like using /as
                                                    // playerid is the id of the player that send the API command
    }
});

Thanks again folks.
P.S. I did some more research and it seems that the issue in my previous post has already been handed over to the devs. No idea on a timeframe for the fix though, and it is a bug, not intentional.