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

Calculating character sheet formulas in API

I have a script that outputs the current value and maximum value of a number of character sheet attributes (skipping attributes that are undefined or have both current and max values of 0.) When I pull the maximum value for, say, hd_d8 (all d8 hit die available to a character), getAttrByName function returns the following value: bard_level + cleric_level + druid_level + rogue_level + monk_level + warlock_level What is the most efficient way for me to calculate this formula? Short of writing a function to parse arithmetical strings? I'd like to be able to include the calculated value in a HTML-marked-up string which I will then output using one sendChat function. The final output would look like: Resources for Fighter Bob Action Surge: 0 out of 1 Second Wind: 1 out of 1 Hit Dice (d10): 3 out of 5 Level 1 Spell Slots: 1 out of 3
1422394968

Edited 1422395233
Lithl
Pro
Sheet Author
API Scripter
Let the dice engine calculate it for you. sendChat('', '/r @{bard_level} + @{cleric_level} + @{druid_level} + @{rogue_level} + @{monk_level} + @{warlock_level}', function(ops) { var msg = ops[0], rolldata = JSON.parse(msg.content); // rolldata.total should be the sum of the attributes }); Note that sendChat is an asynchronous function, so you'll need to either perform the rest of your script's calculations in the same sendChat callback, impose some reasonable delay before continuing your code, or implement a semaphore . ETA: You may need to edit the string to specify the character name, eg: // Assuming the variable `hd_d8` exists, storing the relevant string // Assuming the variable `character` exists, storing the relevant character object hd_d8 = hd_d8.replace(/@\{(.+?)\}/, '@{' + character.get('name') + '|$1}'); sendChat('', '/r ' + hd_d8, function(ops) { ... });
Hi Brian, Thanks for this! That semaphore was a bit over my head, but I'll figure it out next time. sendChat was hanging up on the attribute calls because they didn't reference a specific character. I'm not sure why the character sheet can handle this internally, but all my calls were failing. So I added a somewhat clumsy: if(isNaN(max)) { max = max.replace(/@{/g, "@{" + character.get('name') + "|"); } And then added [[]] around the variables in my output: output += '<tr><td><b>' + attributeName + ':</b></td><td> [[' + value + ']] out of [[' + max + ']]</td></tr>'; And that seems to work.
1422400506
Lithl
Pro
Sheet Author
API Scripter
redrick W. said: sendChat was hanging up on the attribute calls because they didn't reference a specific character. I'm not sure why the character sheet can handle this internally, but all my calls were failing. Well, the character sheet knows which character the attributes are applying to. The API doesn't. =) I'm glad you've found a solution that works for you.
Hey Brian, I'd actually love to hear a little more about how you would use a semaphore in an application like this. I've got another situation where I would really like to be able to pull data from a character sheet and then continue working with it in my function. The function is a little too complicated for me to figure out how to wrap the whole thing inside of a sendChat() callback function. It would be really nice to be able to call a function calculateFormula which would be able to call the sendChat, wait until it has run, and then return the result of the calculation.
1422672490

Edited 1422672767
Lithl
Pro
Sheet Author
API Scripter
There are two basic ways to use the semaphore: Initialize the semaphore with the number of asynchronous tasks you plan to use Call the v() function just before initializing an asynchronous task. At the end of each asynchronous task, call the p() function. The main difference between the two approaches is that the former isn't useful if you don't know how many asynchronous tasks you're going to launch, and the latter might fire the semaphore's callback multiple times, depending on how long your main "thread" takes and how long your asynchronous tasks take. An example of the former: var sem = new Semaphore(function() { log('All three async actions are complete'); }, 3); sendChat('', myFirstRoll, function(ops) { // Deal with the results of `myFirstRoll` sem.p(); }); sendChat('', mySecondRoll, function(ops) { // Deal with the results of `mySecondRoll` sem.p(); }); sendChat('', myThirdRoll, function(ops) { // Deal with the results of `myThirdRoll` sem.p(); }); You can't control the order in which the three sendChat calls will complete, but you're guaranteed that all of them are complete when the Semaphore callback runs. An example of the latter method: var sem = new Semaphore(function() { log('All running async actions have completed, but you might add some more and this will be called again'); }); _.each(myArray, function(item) { if (item.myProperty) { sem.v(); sendChat('', item.myProperty, function(ops) { // Deal with the results of `item.myProperty` sem.p(); }); } }); The linked implementation of Semaphore above also accepts a "context" object in the constructor; if you supply it, then using the keyword this in the semaphore callback will refer to the context object (otherwise, this will refer to the callback function itself). The linked implementation also allows your callback method to accept parameters. If you pass any extra parameters after context to the constructor, those parameters will be used as the defaults for the callback. If you supply any parameters to the p() function, those parameters will override the defaults given in the constructor. There's an example of that on the linked wiki page. (Note that if you're passing parameters to the callback through p() , only the parameters from the last p() will be used; you can use this to figure out which asyc operation finished last.)