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

[Help] Get roll total in the API

December 12 (10 years ago)

Edited December 12 (10 years ago)
Right, so I figured this would be a quick and simple thing to learn API with but I am starting to pull out my hair. The idea goes like this; I wanted to make an API to convert this:
!Heal @{target|token_id} HP 1d8+1 (or even [[1d8+1]] doesn't really matter either way)
And make it check to see the characters current HP, compare it to its maximum. Take the value of the roll. Add it to the current. And make sure it doesn't go over the characters max for a quick and easy healing API using the targeting system for a party healer.

This is how far I got.
var Healspell = Healspell || {};

on("chat:message", function (msg) {
    //log(msg.content);
    if (msg.type != "api") return;
    
    var args = msg.content.split(" ");
    var command = args.shift();
    
    if (command === "!heal") {
        log(args[0]);
        var token = getObj("graphic", args[0]);
        var char_id = token.get("represents");
        var attr_name = args[1]
        var curVal = parseInt(getAttrByName(char_id, attr_name));
        var maxVal = parseInt(getAttrByName(char_id, attr_name, "max"));
                
        log(!char_id);
        log(attr_name);
        log(curVal);
        log(maxVal);
        log(args[2]);
        
         sendChat('', args[2], function(ops) {
             log(ops[0]);
             //var rollresult = JSON.parse(ops[0].content);
            // log(rollresult);
            // var total = rollresult.total;
            // log(total);
            // outs[0] is a message object containing the results of the die roll in parts[0]
            // This sendChat message will NOT appear in the actual chat window
        });
        
    }
}); 
Now, I can get everything working until the sendChat part, after that it gets a little wonky.
What it spits out when I do it:
"-JcTj4b-IuhZJ8wNUw-F"
false
"HP"
45
78
"1d8+1"
[{"who":"","type":"general","content":"1d8+1","playerid":"API","avatar":false,"inlinerolls":{}}]

Now, when everything is said and done I try and do the rollresult, and it always crashes. Everything everywhere I've read about this says to do it this way and I have no idea what I'm doing wrong. Please someone help me. Also, I know it probably looks like crap. I'm a novice to java, and this is my first hand-made API.
December 12 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
I can't say what the precise error is you're getting, but your example message of "1d8+1" would need to be "/r 1d8+1" or "[[1d8+1]]" to result in a roll. Likely fixing that will make the error more obvious. Also, if you take the roll in the inline form to your script, you won't need to sendChat() to get the result as it will be in the msg.inlinerolls property.
Perfect! I couldn't figure out how to do it before because after I did a .split() function to pare out the msg into an array, args[2] always returned $[[0]]. But now I see if I get the information for the roll before I split it out I can get the msg.inlinerolls property. Now my only problem is how to pull the total from, var roll = msg.inlinerolls;
December 12 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
I posted something about that earlier this week:

Here's a snippet I use frequently to replace all the inline rolls in a command with their values (you want to be sure and clone the msg if you are doing this so you don't change it for everyone.):
		if(_.has(msg,'inlinerolls')){
			msg.content = _.chain(msg.inlinerolls)
				.reduce(function(m,v,k){
					m['$[['+k+']]']=v.results.total || 0;
					return m;
				},{})
				.reduce(function(m,v,k){
					return m.replace(k,v);
				},msg.content)
				.value();
		}
December 12 (10 years ago)

Edited December 12 (10 years ago)
I'm going to be honest. I'm still very novice at this. I have no idea how to utilize that. I get the basic gist of what it's doing (kind of, but not really) But, just like before, I have no idea how to pull information out of it.
Actually, no. Buggered around a bit with log(); checks and figured it out.
Endless thanks for you and your awesome skill.
December 12 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
No worries, I'm here for you man. =D

If you change the first part of your script to this:

on("chat:message", function (msg_orig) {
    //log(msg.content);
    var msg = _.clone(msg_orig);

    if (msg.type != "api") return;

    if(_.has(msg,'inlinerolls')){
        msg.content = _.chain(msg.inlinerolls)
            .reduce(function(m,v,k){
                m['$[['+k+']]']=v.results.total || 0;
                return m;
            },{})
            .reduce(function(m,v,k){
                return m.replace(k,v);
            },msg.content)
            .value();
    }

    var args = msg.content.split(" ");
    var command = args.shift();


Then args will have the total in place of where ever the inline formula is written.



It's not necessary to understand how this works to use it, but here is a deep description just in case you want to know:

Inline rolls are processed by Roll20 before passing the msg into the API on() handlers. Where ever an inline roll was in the content will have a placeholder of the form $[[#]] instead of the formula. The full details of the rolls for the formula are then put into entries in msg.inlinerolls array, with the index the same as the # in the $[[#]] placeholder. So, the first inline roll will be $[[0]] and explained in msg.inlinerolls[0], etc. So, here's a likely translation from an API command to a message object:
!Heal @{target|token_id} HP [[1d8+1]]
will become this message object:
{
.  "content": "!Heal -JbjeTZycgyo0JqtFj-r HP $[[0]]",
.  "inlinerolls": [
.  .  {
.  .  .  "expression": "1d8+1",
.  .  .  "results": {
.  .  .  .  "resultType": "sum",
.  .  .  .  "rolls": [
.  .  .  .  .  {
.  .  .  .  .  .  "dice": 1,
.  .  .  .  .  .  "results": [
.  .  .  .  .  .  .  {
.  .  .  .  .  .  .  .  "v": 2
.  .  .  .  .  .  .  }
.  .  .  .  .  .  ],
.  .  .  .  .  .  "sides": 8,
.  .  .  .  .  .  "type": "R"
.  .  .  .  .  },
.  .  .  .  .  {
.  .  .  .  .  .  "expr": "+1",
.  .  .  .  .  .  "type": "M"
.  .  .  .  .  }
.  .  .  .  ],
.  .  .  .  "total": 3,
.  .  .  .  "type": "V"
.  .  .  },
.  .  .  "rollid": "-Jd-KnVaVKlWr8me76q-",
.  .  .  "signature": "79e11b47e3b8a6e6ecbfd31d87785006b0eb3f4cb247daad6282178f12f7b10f2a6b2b05a8cc566da25719084c984a5f813403235446e8420648590597e9f632"
.  .  }
.  ],
.  "playerid": "-JS3qKxIUPHLzSbK24ve",
.  "type": "api",
.  "who": "The Aaron"
}
Indices in arrays are implied in JSON notation, but you can see the object that is the details of the first roll, it take sup most of the block above.

The block of text I have above explained:
    if(_.has(msg,'inlinerolls')){    // Check that there is an inlinerolls property on msg
        msg.content = _.chain(msg.inlinerolls)    // use the _.chain() command to walk the 
                                                  // list of inline rolls (1 in this case)
                                                  // Each chained function gets the results
                                                  // of the previous as it's input.  The result
                                                  // of _.chain is the final value of the chain,
                                                  // which is returned by _.value()

            .reduce(function(m,v,k){        // _.reduce() calls a function on each element
                                            // in the current chain, building a result object
                                            // that becomes the input to the next function.


                m['$[['+k+']]']=v.results.total || 0;  // m is the current state of the reduce,
                                                       // passed in initially as {} below, but
                                                       // then is the result of each call of
                                                       // the function parameter to _.reduce().
                                                       // this function takes the results.total (or 0) and
                                                       // puts it in the reduce state with the property
                                                       // named for what the placeholder is (k is the index
                                                       // into the msg.inlinerolls, v is the current roll).
                return m;        // returns the current reduce state for the next iteration
            },{})            // this is where the initial reduce state is set, {}.  It becomes m in the call.
            .reduce(function(m,v,k){           // this reduce is taking the map from placeholder to total
                return m.replace(k,v);         // and doing a string replace of the placeholder (k) with the 
                                               // total (v) in the original message content (m)
            },msg.content)     // the initial reduce state is the msg.content, which becomes m above
            .value();   // this returns the msg.context with the placeholders replaced, allowing it to be assigned
                        // as the return from _.chain().
    }

Hope that helps!
December 12 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter

Kerberos said:

Actually, no. Buggered around a bit with log(); checks and figured it out.
Endless thanks for you and your awesome skill.

Ah, great! Even better! Sorry, I got interrupted while typing my answer. You can insert .tap(log) between operations in .chain() to see the current state of the chain. that really helps in seeing what's going on.
No problem. This is wickedly in depth and I love it. Now I only have one problem left, if you'd be so kind as to help one more time it would be fantastically appreciated.
When trying to get the max value of an attribute in my code I have these lines;
        var curVal = parseInt(getAttrByName(char_id, attr_name));
        var maxVal = parseInt(getAttrByName(char_id, attr_name, "max"));
Now, if the current and max values of an attribute are different it throws out the proper answer. Ex. curVal=32, maxVal=55. But if they're the same I will not get a log(); for maxVal. What's up with that?
December 12 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
hmmm. You might need to repost your code. Nothing looks wrong to me with those two lines. (except, you should probably pass in the radix for parseInt(getAttrByName(char_id,attr_name),10); )
I think I figured it out (kinda). For some reason it wont do log(curVal); immediately followed by log(maxVal); for only one character on the screen. It will still memorize the value and I can still tinker with it, like adding or subtracting numbers. But for some reason it just won't log out. Weird.
Oh well. As long as it works, thats all I care for now. It's so nice to finally get this API up and running, with so much thanks to all your help. I look foreward to making a ton of specialized API for my campaign so I can quit asking people like you in the forums to make it for me. Although I do look forward to seeking help from you and others in the future when one problem or another stumps me.
Thanks again!
December 13 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
no problem. =D
December 14 (10 years ago)
Will you past the finished code please if i already post i am sorry for asking.

I want to use this for auto health tracker for my character.

So if i take -10 damage then I could use the Marco to auto change my hp so i do not have to type it in and do math.

I know that is lazy yet i want it to be there to use if i want to use it.


Then I could also use this for other things like if I take off my shield then i would just decrease the shield amour by 2 to make it zero with in the character sheet.

So many endless ways of using this.


Thanks,

Levis W.
Sure thing. Its super rough but I'll post it with a brief description of how to use it so out doesn't break. I'm not very good at it do it doesn't have a lot if intuitive lines in case things are wrong. I'm away from right now but i should have it up by tonight.