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

delay in return from function call?

1411970952

Edited 1411971004
I am just getting started in some personal scripting using the API, but I am wondering if I am doing something wrong here. I am trying to call a function to get the result of a die roll then store it to a variable for later use, but it seems to be delayed. Is there a better way to store die roll results for later use? Here is some simple code that can be used to show what I am seeing. on("chat:message", function(msg) { var rollvalue = "0"; if(msg.type == "api") { if (msg.content == "!attack") { var rollvalue = dieroll("d20"); log(rollvalue); sendChat(msg.who, "roll result is " + rollvalue); }; }; function dieroll (roll) { sendChat(msg.who, "/roll 1" + roll, function(ops) { var rollvalue = JSON.parse(ops[0].content).total; log(rollvalue); return rollvalue; }); }; });
1411998506

Edited 1412006848
The Aaron
Roll20 Production Team
API Scripter
sendChat () is asynchronous, meaning the call will return immediately, but will call the anonymous function in the background at some later point in response to some event. You can observe this behavior by putting in some additional log statements: ( Sample 1 ) on("chat:message", function(msg) { var rollvalue = "0"; if(msg.type == "api") { if (msg.content == "!attack") { var rollvalue = dieroll("d20"); log(rollvalue); sendChat(msg.who, "roll result is " + rollvalue); }; }; function dieroll (roll) { log('dieroll(): Before sendChat()'); sendChat(msg.who, "/roll 1" + roll, function(ops) { var rollvalue = JSON.parse(ops[0].content).total; log('dieroll(): Inside sendChat()'); return rollvalue; }); log('dieroll(): After sendChat()'); }; }); Which will log: dieroll(): Before sendChat() dieroll(): After sendChat() undefined dieroll(): Inside sendChat() (the undefined is from logging the results of dieroll() , which doesn't return anything and so rollvalue will be undefined.) Depending on what you are wanting to do, you can either use the randomInteger () function to generate an immediate random number between 1 and the passed in value, or you can restructure your code to use the callbacks. Restructuring feels "wrong" if you're used to procedural languages, but I promise you it's not as bad as it seems. =D Here is your code restructured ( and you don't need the JSON.parse() Oops! You do need it for /roll, not for inline rolls. ): ( Sample 2 ) on("chat:message", function(msg) { if(msg.type == "api") { if (msg.content == "!attack") { sendChat(msg.who, "/roll 1+d20", function(ops) { var rollvalue = JSON.parse(ops[0].content).total; sendChat(msg.who, "roll result is " + rollvalue); }); }; }; }); Probably that looks too simple, and it really is. You will probably find you would rather use inline rolls, so that you can chain several together and not need to nest multiple sendChat() calls: ( Sample 3 ) on("chat:message", function(msg) { if(msg.type == "api") { if (msg.content == "!attack") { sendChat(msg.who, "[[1+1d20]] [[2d6+3]]", function(msg2) { var attackroll = msg2[0].inlinerolls[1].results.total, damageroll = msg2[0].inlinerolls[2].results.total; sendChat(msg.who, "roll to hit: "+attackroll+" for "+damageroll+" damage."); }); }; }; }); Hope that helps!
1412004197

Edited 1412004241
That does help thanks. The reason I went down this road is because initially I was doing basically what you mentioned in the first scenario but it appeared I was unable to use the value assigned to rollvalue later. This could be because of what you mentioned before with the actual assigning happening later. In your first example, how can I use that rollvalue value later? Just using the code block you have it is still coming back as undefined, but I am looking to do something like this, the point being that a variety of things could happen or I could be making various rolls before I format them all into a pretty display. on("chat:message", function(msg) { var rollvalue = "0"; if(msg.type == "api") { if (msg.content == "!attack") { sendChat(msg.who, "/roll 1+d20", function(ops) { var rollvalue = ops[0].total; }); sendChat(msg.who, "roll result is " + rollvalue); }; }; }); Also, your second example is giving me the same results, undefined value. This is basically what prompted the question for me, because I agree it seems like these should work, but I am getting undefined back. on("chat:message", function(msg) { if(msg.type == "api") { if (msg.content == "!attack") { sendChat(msg.who, "[[1+1d20]]", function(ops) { var attackroll = ops[0].total; sendChat(msg.who, "roll to hit: "+attackroll); }); }; }; }) Now if I go with something like this it works fine, assuming I do something with the return right away, but I do not want to do that. on("chat:message", function(msg) { if(msg.type == "api") { if (msg.content == "!attack") { sendChat(msg.who, "/r 1d20", function(ops) { var attackroll = JSON.parse(ops[0].content).total; sendChat(msg.who, "roll to hit: "+attackroll); }); }; }; }); I want to do something with the roll outside of the sendChat, so I can do things with it later. Just modifying the one that works right above this to move the sendchat outside of the other sendchat it does not store the value again. on("chat:message", function(msg) { if(msg.type == "api") { if (msg.content == "!attack") { sendChat(msg.who, "/r 1d20", function(ops) { var attackroll = JSON.parse(ops[0].content).total; }); sendChat(msg.who, "roll to hit: "+attackroll); }; }; }); Long story short, sounds like I should use the random integer, do you agree?
1412007091

Edited 1412007117
The Aaron
Roll20 Production Team
API Scripter
I'm slightly confused about which ones you're referring to, so I went back and numbered the original post. I'll answer based on my guesses. The first example (Sample 1 above) is only meant to illustrate the order of operations via the log statements. That was just to explain what was happening. rollvalue will not be set to anything other than undefined in that sample. Regarding Sample 2: Oops! I'm too used to doing the inline rolls. You DO need it for the /roll version, I corrected the above sample. Regarding Sample 3: Boy, I really screwed the pooch on that one. Revised and tested, it should work correctly now. Sorry! That's my fault for just typing out the code from memory. =D So, to get back to your original questions: Using sendChat() to parse rolls is a godsend if you are accessing attributes and supporting all the various mechanics on dice, like rerolling and such. Depending on what you are doing, I would suggest building several inline dice expressions in the first part of your api command, then send them all to chat and harvest all of them in the second part of your script as part of the callback to sendChat(). (And sorry about all the bugs!!)
No worries, you have been very helpful. I think the answer is to either use the random function or nest a bunch of sendchat. The second is really hard to follow and sloppy so random it is. Basically if you get into a situation where 1 die roll can affect or spawn other die rolls you will have a huge mess of nested sendchats if you try to adhere to the roll20 way of doing things. In most cases this is probably not a huge deal, but in a setting where a lot of random things can happen it would become a code management nightmare. Again, thanks for your help!
1412010277
The Aaron
Roll20 Production Team
API Scripter
Nesting things via a callback can be very confusing certainly. I don't know about your circumstances, but I would be tempted to just build every die roll, then use logic to determine the results. So for example if you were doing a DnD 3.5 rule version: a roll for to hit a roll for to hit to confirm critical a roll for base damage a roll for critical damage Then in the callback function: if the first roll is a hit check if it was a critical check if the second roll was a hit damage is the sum of the 3 and 4th rolls otherwise the damage is just the 3rd roll. Could get complicated, but you could build a queue of processing functions as you build a queue of rolls, then execute the queue across the results. That might get a bit complicated, but no more complicated than implementing all the dice logic you might want. Another option is to just have the user pass in the various die rolls and pick through the results as desired: !attack [[1d20+1]] [[1d20+1]] [[2d6+3]] [[2d6+3+1d8]]
1412017105

Edited 1412017138
Oh yeah, that would actually work great. This particular instance is for DCC which has different tables you roll on for crits, fumbles, and varying damage based on those rolls also. However, basically doing every possible roll at 1 time would be the optimal solution. Good call, I will do it that way, thanks for the help, you have gone above and beyond, much appreciated.
1412017299
The Aaron
Roll20 Production Team
API Scripter
No worries, happy to help! =D
1412020043
Gen Kitty
Forum Champion
Psssst, Aaron. You'd want to break out sneak attack dice, and elemental damage dice too, because in 3.5 you have critters immune to precision damage, and possibly immune to the elemental damage(s) of your weapon. 4e is... easier that way :> But if this meant I could have a macro spit out: [3] base weapon damage, [7] precision damage, [4] Fire Damage for [14] total, I would be all over that like white on rice for the Pathfinder game I'm in. x.x
As part of an API that would be pretty simple to do, but I do not think you could do it as just part of a macro.
1412022232

Edited 1412035595
Using the same example that Aaron started, you would be looking for something like this. on("chat:message", function(msg) { if(msg.type == "api") { if (msg.content == "!attack") { sendChat(msg.who, "[[d20]] [[2d6+3]] [[d8+2]] [[d8+3]]", function(ops) { var attackroll = ops[0].inlinerolls[1].results.total, base = ops[0].inlinerolls[2].results.total, precision = ops[0].inlinerolls[3].results.total, fire = ops[0].inlinerolls[4].results.total, total = base+precision+fire sendChat(msg.who, "<table style='background-color:black;\ color:white;\ border-radius: 15px;\ width:100%;\ text-align: center'> \ <tr style='background-color:#9DD929;text-shadow: 1px 1px 1px #568F23;'>\ <th style='border-top-left-radius: 15px'>Roll Reason</th>\ <th style='border-top-right-radius: 15px'>Value</th>\ </tr> \ <tr style='background-color:#DEF3CA;color: #666'>\ <td>Attack Roll</td>\ <td>" + attackroll + "</td>\ </tr> \ <tr style='background-color:#DEF3CA;color: #666'>\ <td>Base Damage</td>\ <td>" + base + "</td>\ </tr> \ <tr style='background-color:#DEF3CA;color: #666'>\ <td>Precision Damage</td>\ <td>" + precision + "</td>\ </tr> \ <tr style='background-color:#DEF3CA;color: #666'>\ <td>Fire Damage</td>\ <td>" + fire + "</td>\ </tr> \ <tr style='background-color:#DEF3CA;color: #666'>\ <td>Total Damage</td>\ <td>" + total + "</td>\ </tr> \ </table>"); }); }; }; });
1412031579

Edited 1412033945
The Aaron
Roll20 Production Team
API Scripter
I think it should be: fire = ops[0].inlinerolls[4].results.total, Looks cool!
yup, copy paste failarino.
1412038599
The Aaron
Roll20 Production Team
API Scripter
Copy/paste is a programmers best friend, until it isn't! =D