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

Looking for how to save last roll(s) results to use in future rolls.

Can you hook the chat to intercept rolls and modify the syntax as it is sent? Or do you have to create a new roll type function to do that? I've been wondering if there would be a way to add a new keyword to the roller, that keyword would be "last" and it would contain the last result of the roller. So, for example, say you're running damage, and you want to have a running total, but it's conditional. Say, critical hits. Write a macro: Attack [[1d20]] | [[1d20]] Damage = [[1d8]] Critical Damage = [[last+1d8]] The output would then have the "proper" results, and no addition is required by the player to total damage. This makes it very simple to make a macro block, and it sums your last damage + your new damage. Now I'm not sure the chances of someone else injecting a ROLL at the perfect time to mess this up. But I figure most of the time macro blocks will run sequentially, and no one could "inject" bad data at the wrong time to throw off the math. If you're feeling froggy, make it a pseudo array of 5 rolls, so you can go back in time a few roll results. Write a macro: Attack [[1d20]] | [[1d20]] Damage = [[1d8]] Sneak Attack Damage = [[2d6]] SubT = [[last1+last2]] Critical Damage = [[last3+1d8]] Critical Sneak Attack = [[last1+last3+2d6]]
1424644246
The Aaron
Pro
API Scripter
You couldn't modify the rolls with the API as the rolls are processed and sent to the chat outside the scope of the API. The API does get the messages, and can inspect them for details, but not change the results. You can look at my AnnounceRoll script for an example of this kind of interaction with the API. If you wanted to do this, you would probably want to write a script that handles generating the output, at which point you are free to make whatever changes to the rolls you like.
I'm trying to catch what the users is trying to roll, and before it is sent to the roller, replace "last1" with a variable. The variable is set based on the result of the last roll result. So I'm not sure I'm following what you're saying. I actually did open your AnnounceRoll script before posting this, and was messing with it. I guess I'm just not clear where the API is intercepting the messages I want to modify it before it's sent to the roller, then take the last roll result, catch that, and store the result.
Seems a terrible work around, but if I can only deal with data that has already come out, and I can't manipulate it going in. I could just take the last roll that came out, and stuff it into a character attribute @{roller|last1}. Then when I write my macro, I just pump in @{roller|last1} to the roller, it will read the character attribute, and roll with the correct modifier, and when the result comes out, all I do is take the total result, and PUSH it back into the stack of last rolls.
1424647880
The Aaron
Pro
API Scripter
Sorry, I'll try to be a bit more clear. =D The API receives every chat message. Chat messages have a type associated with them (One of "general", "rollresult", "emote", "whisper", "desc", or "api".). In the case of AnnounceRoll, it is looking at rollresult messages, which is what type "/r 1d20+3" has. All chat message types except api are processed and sent as output to the chat. This is why I said you could not change the output for those macro messages. api messages are never processed automatically and sent to chat. The API gets them, and must issue messages if output is appropriate. This is why I said you would need to write a script that generates the output. My ZombieDice script takes rolls from the user, then manipulates them to produce output. Ripshot said: Seems a terrible work around, but if I can only deal with data that has already come out, and I can't manipulate it going in. I could just take the last roll that came out, and stuff it into a character attribute @{roller|last1}. Then when I write my macro, I just pump in @{roller|last1} to the roller, it will read the character attribute, and roll with the correct modifier, and when the result comes out, all I do is take the total result, and PUSH it back into the stack of last rolls. I think what you are saying is something along the lines of: Attack [[1d20]] | [[1d20]] !lastroll 1 [[1d8]] Damage [[@{selected|last1}]] Critical Damage = [[ @{selected|last1} +1d8]] However, this will not work. Commands are evaluated in a particular order . In the case of the above, the first thing that would happen is the @{selected|last1} would be replaced with the current value of that attribute, then all the inline dice expressions would be evaluated (rolled), then each row of the macro would be issued to the API, and the non-api ones also issued to the various chat panels. Hope that helps. Let me know if you have any more questions! =D
Well, your second output was close. I was thinking of this. Make a roller intercept. on message type roller nab value push that value onto the stack in a character attribute tree named "roller" "last1" 2, etc. take attribute "last6" and drop it off the stack... So to speak. Now that is it, that is all the API does. It grabs the last roll. Pushes it onto the roll stack in a character. Then I can write all my macros, without any other API help. Macro: Attack: [[1d20]] | [[1d20]] Damage: [[1d8]] Critical: [[@{roller|last1}+1d8]] That should work right? Because the moment the "DAMAGE" roll comes out, my api will see it, stuff it into a character sheet attribute. The next line of macro going into the msg event is "CRITICAL" it will auto pull the attribute I just wrote, so it should basically add the two rolls together. This of course falls apart if when you send a macro it's all one HUGE msg, and not 3 distinct msg that the API can eat and add attributes to the character.
1424649284
The Aaron
Pro
API Scripter
So, for your example: Attack: [[1d20]] | [[1d20]] Damage: [[1d8]] Critical: [[@{roller|last1}+1d8]] If you issued this as 3 separate macros (i.e.: 3 separate button presses), it could work (provided only one person was doing this at a time). However, if you have this as 3 lines of a single macro (i.e.: 1 button press), it will not work as @{roller|last1} will be filled in before any of it is issued to the API.
Would it work if I build each line as a separate macro, then build a wrapper macro? #Atk #Dam #crit Wrapper Macro #atk #dam #crit
1424649826
The Aaron
Pro
API Scripter
no. Order of Operations While the Roll20 dice engine does support basic math and functions such as floor() and ceil(), it is first and foremost a dice engine, and so it has its own order of operations. This means that putting parentheses inside of your dice formula will not always affect the outcome of the roll (for example, you can't force a variable to be interpreted before a macro). Here is the general order of operations: Abilities are expanded (meaning the definition of the ability is placed in the formula anywhere that ability appears). Macros are expanded, including nested macros up to 99 levels deep. Variables are substituted Roll queries are executed (the player making the roll is asked to provide a value for each query, and that value is substituted in where the roll query appears in the formula) Inline rolls are executed, and the overall result of the inline roll is substituted in wherever the inline roll appeared in the formula. The remaining roll is executed: first, dice are rolled for any dice (e.g. "2d6" is rolled; including any special dice such as dropped or exploding), then the result of that roll is substituted into the formula. Next, floor() and ceil() functions are executed. Finally, the entire remaining formula is evaluated, including observing proper math order of operations (parentheses first, then multiplication/division, then addition/subtraction).
:'( Feature request it is...
1424650757
The Aaron
Pro
API Scripter
You can still do this via the API, you just need to issue the messages to display the output.
1424651919

Edited 1424655167
Well even the most simple of scripts is beating me up. on("chat:message", function(msg) { if(msg.type == "api") { sendChat(msg.who, "Did this even work?"); } }); It will run sometimes, and only sometimes, it may run 2x in a row then never respond, it may run 4x. I'm self taught here, so goofy results with no error logging just makes this an effort in madness.
1424655302
The Aaron
Pro
API Scripter
Try: on('ready',function(){ on("chat:message", function(msg) { if(msg.type === "api") { sendChat(msg.who, "Did this even work?"); } }); log('Test Script Ready.'); }); and make sure you get 'Test Script Ready.' in the API Console before you issue your !command.
Well, that would explain it... You have to wait upwards of a full minute before this thing does anything. I was trying to rapidly prototype changes. Build a search replace, test rolls, write things, and kept changing my script every 15 seconds. yeah, not so much. I'll need to write in an on ready just to ensure I know what is up. You've been a huge help today, thanks. Now that I know I have to go super slow... and test slow...It won't be such a mystery.
1424671641
The Aaron
Pro
API Scripter
No worries, I'm happy to help! :) It's usually best to do your event subscriptions in an on('ready',...). It's not such a necessity with chat events, but creation events occur fo each thing in the campaign on startup which you usually don't want to deal with. Usually the API is much more responsive, it seems to be having some issues right now. The weekends are often a bit laggy. Week nights and mornings are usually pretty speedy. :)
You've been a huge help really. I was able to muddle my way threw attribute creation, pulling data, checking rolls, and a few other things. inline rolls still eludes me, but now that I've seen how attributes work, I'm sure they are probably very similar. It really sped up as the night drug on! I was able to test about 100 things that didn't work to find the few that did. (I realize I'm not checking for errors, or types, or 20 other things right now, so it's sloppy sloppy sloppy, but I got several things working.)
1424698977
The Aaron
Pro
API Scripter
No worries. Glad to hear you experienced so successes! The API can be very fun and rewarding. :) Inline rolls are attached to the msg object passed to your chat handler, the are discussed here: <a href="https://wiki.roll20.net/API:Chat" rel="nofollow">https://wiki.roll20.net/API:Chat</a> Be be sure to pay attention to the difference between inline rolls ( [[ dice ]] ) and origRolls ( /roll dice ). Here is a function I wrote that returns the msg.context with the inline roll placeholders replaced with their values: <a href="https://wiki.roll20.net/API:Cookbook#processInline" rel="nofollow">https://wiki.roll20.net/API:Cookbook#processInline</a>... The basic idea to understand with inline rolls is that wherever they were placed in the command will be replaced by a placeholder like [[$0]], wferever the number is the index into msg.inlinerolls where that roll's data is located.
The largest problem I'm having... Is I haven't ever coded in Javascript, knowing all the built in methods to accomplish my tasks. All this _.has _.reduce, and what appears to be creating functions on the fly has me baffeled. function(things, seems, random). It's almost as if your creating the function to handle the output results on the same line you're taking in the results. So if you don't know it's going to hand you 3 things, and I've never created a function seemingly on the fly to process results.
1424712427
The Aaron
Pro
API Scripter
Ah, yeah, I can see how that would be confusing. Now is the time when I generally say: I highly recommend the book Javascript: The Good Parts by Douglas Crockford . It's a dense, but very good book about Javascript and how to use it and understand it. I think it's a quick easy read, but some have expressed that it is a bit difficult for them to get through. YMMV, but I think it's worth the read. So, just to move you along prior to reading it, I make heavy use of the underscore library for javascript. It's full of powerful functionality. In javascript, everything is an Object (though some have special extra syntax). Even numbers are objects: 10.3.toString(); // returns '10.3' Functions are objects, and so can be created and passed around like anything else. var Foo = function(){ return 'foo'; }; Foo(); // returns 'foo'; Functions without names are called anonymous functions. You will often see anonymous functions passed to other functions to perform some operation (It's basically the visitor pattern in computer science). You also see them sometimes executed immediately: var foo = foo || (function() { return { foo: 'bar' }; }()); This is important because of the concept of Closures , which is the way you have a private scope in Javascript: var counter = (function() { var num = 0; return { getCount: function() { return num;}, incCount: function() { ++num; } }; }()); counter.getCount(); // returns 0 counter.incCount(); // changes num to be 1; counter.getCount(); // returns 1 At this point, num can only be accessed by the getCount() and incCount() functions because they have access to Closure of their parent function (the anonymous function), but nothing else does. One really useful technique when dealing with passing anonymous functions passed to other functions is the arguments object, which has all of the arguments passed to a function. You see, you can pass as many arguments to a function as you like, regardless of what it declares in it's parameter list. So, if you want to see what _.each() is passing to the callback function, you can do this: _.each([1,2,5,8,23],function(){ log( arguments ); }); Hope that helps!
It does, and it makes sense. I'm just going to have to get used to it. I've done mostly functional programming, with light object use. Never any "visitor patterns". Most everything I write, I allow direct access. For example I expect once I nab a character... To be able to access the char.name directly without using a method. However, here. I have to nab the character, then use the .get('name') to pull the name. Not even really an extra step, just a different method of accessing the data. In my mind, that is more complex and confusing, because I've formed years of bad programming habits, self coding anything I can sort out. (Just like I'm doing now... spend hours and hours working out the puzzel without any courses or foundational work to know anything about good practice.)
1424720378
The Aaron
Pro
API Scripter
Encapsulation in a private scope is nice as it prevents external users from depending on implementation details of your object (I actually got burned by that this weekend as GenKitty was having problems with a script I hacked together which used a function HoneyBadger's PowerCards script defined.. then she didn't have PowerCards on the campaign and the hacked script blewup... whoops! ). That means you can change them later without breaking users, provided you don't break the contract of your interface. =D The reason for the .get() on the Roll20 objects is likely more because they need .set() functions. Since all the Roll20 objects are stored in a database, doing this gives them the opportunity to know when the object is changed (you called .set() on it ( *cough* template method pattern *cough* ) ) as opposed to having to do some sort of polling or requiring you to trigger a save with some sort of .save() method. Since Javascript doesn't provide any way to overload the setting of properties like some languages, this is the best possible solution. If you've got any questions, I'm happy to help with them! =D
1424753781

Edited 1424754288
Well it turns out, that the on message event will fire multiple times per block. It actually fires once per line, and the inlinerolls stays persistent for each fire. So if I do something like this... on("chat:message", function(msg) { if(msg.type === "general" && msg.inlinerolls ){ log(msg.content) }}); You get three fires... once for each line that has a roll. Kinda strange... However this does make it so it is almost trivial to total rolls you mark... If you add some syntax say... + directly before a roll, you can find that roll in the content, pull the array index, use that on the inlinerolls to pull the total of that roll, and do that line by line. You know you're @ the end, when your content has an inline roll with a index that matches the last index in inlinerolls. So if you do something like... Attack [[1d20]] or [[1d20]] Damage +[[1d8+5] if crit +[[1d8]] Your on message will fire 3 times. The 1st time nothing on interest for a sum. The second time, it will fire and you can capture that roll result total. The third time, it will fire and you can see the final total, and know you're @ the last roll because the index match the max index of inlinerolls. However... Because I'm doing this all in a function(msg){} I can't remember my state. Each fire of the function, I lose all my variables. So I get two total prints... one for the damage line, one for the crit line. So, I'm assuming that is why you write code with functions, and then register it to the on message event, so you can save variables and reuse them and keep some state?
Duh, just declare my var outside the event... Solved.
1424755058
The Aaron
Pro
API Scripter
Right. So in my scripts, I store variables I'll use across events in the closure that holds my functions. Usually people write handlers for api messages. There are a few reasons for this, but the first one you're likely to hit is when you use sendChat() to issue output which is of type general and triggers your script to issue output which is of type general and triggers your script to issue output which is of type general and triggers your script to issue output which is of type general and triggers your script to issue output which is of type general and triggers your script to issue output... API Commands to not issue output, so the only way you'll receive them is if a user issues them. The second one you're likely to hit is that any chat is general. So when your player types "Bob hides under the table. Hide: [[1d20+5]]", it's going to trigger your script. You could add some sort of pattern matching to determine if what they typed fits with what you're intending to process, but at that point, you're kind of reinventing the wheel. You could instead write your script to process something like this: !calc-attack --attack|[[1d20]] [[1d20]] --damage|[[1d8+5]] --crit|[[1d8]] with a minimum of effort. Then you have everything you need to work with and can issue the resultant message fairly easily.
Yeah, I've already run into about... all those problems. It was awesome when the game went infinite loop dumping 18 for 6 pages before I could click save script to kill that instance. It's currently outputting what I want, and how I want, without going nuts. I realize because it's a general message, with an inline roll the event is firing a lot. But technically all chat and rolls fire it. So even the output of the !API method would generate onMessage events. Right? I'm not hooking them, but they are there. Here is what the output looks like: Ripshot (GM): Attack: 10 or 17 Damage: +6 if this is a crit +5 Total: 11 Super clean, nothing to remember except all rolls you want to total just put a + in front of the inline roll.
1424784626
The Aaron
Pro
API Scripter
Yes, all messages are passed to all message handlers. Glad to hear you got it working. :) Something else to be aware of: the same msg object is passed to every message handler. This means if you change the msg object, it is changed for all the message handlers called afterwards.