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 .
×

Calling scripts with macros

1480037124

Edited 1480037150
Hello everyone, Macros in this appear to executed line by line, as they should and makes sense. But if I have a macro calling a script upwards of 6 times, calls to the script appear to get lost on occasion. Further, if I have the macro call a script and then say something, it will say something before the script executes. I'm guessing these two things coincide and would be fixed by adding a brief pause in between lines in the macro, but I'm skeptical that there would be anyway to do that? For specifics: I'm using a slightly modified version of this script:&nbsp; <a href="https://app.roll20.net/forum/post/551713/script-si" rel="nofollow">https://app.roll20.net/forum/post/551713/script-si</a>... One example of something I'm trying to do: one of the players in the game has a form change, that adjusts his base stats, which cascades into a change in defenses, and then I display in a template the new stats. The first issue with this example, is that the template appears first, displaying old stats, and then the stats change. This can be solved by make the two separate macros, but the point of a macro is sort of to be all in one click. I thought maybe I could fix this by splitting the macro into two, and then making a new one that called them in order, but that doesn't help either. The second issue with this example, is that when there is a long list of attribute changes (I've only noticed when 6 or more attributes are changing), some of the attribute changes get lost. Most frequently the lost ones are towards the end of the list in the macro. My best guess is that the macro is calling the script again before the last call finished, but I'm not really sure. Does anyone have any ideas of how to get around these issues? Or perhaps you want to correct my understanding of what causes them? If I am right about the cause of both issues, then being able to add a delay between line calls in the macro should solve my problems. Is there some way that I'm missing that I can accomplish this? Thanks in advance everyone!
1480046388
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
You'll need to make two independent macros to get the output that you want. Macros are actually evaluated all at once, but each line counts as a new message for the API (hence the ability to send multiple API commands. See the&nbsp; order of operations page in the wiki for details on how things are expanded. As for the attribute calls getting lost, it'd help if you could post your macro. Scott
1480047778

Edited 1480051334
Here's a simple example of a macro changing attributes where on occasion attribute changes are lost: !attrib ac-ability|Math.floor(((@{dexterity}+@{intelligence}+Math.abs(@{dexterity}-@{intelligence}))/2-10)/2)*@{armor-is-light} !attrib fort-ability|Math.floor(((@{strength}+@{constitution}+Math.abs(@{strength}-@{constitution}))/2-10)/2) !attrib ref-ability|Math.floor(((@{dexterity}+@{intelligence}+Math.abs(@{dexterity}-@{intelligence}))/2-10)/2) !attrib will-ability|Math.floor(((@{wisdom}+@{charisma}+Math.abs(@{wisdom}-@{charisma}))/2-10)/2) !attrib hp-max|(@{base-hp}+@{hp-feats}+@{hp-per-level}*(@{level}-1)+@{constitution}) !attrib hp|#@{hp-max} !attrib hp-bloodied|Math.floor(@{hp-max}/2) !attrib surge-value|(Math.floor(@{hp-bloodied}/2)+@{surge-value-bonus}) !attrib surges|#(Math.floor((@{constitution}-10)/2)+@{base-surge-num}) This one is simply to adjust stats that my character sheet doesn't automatically adjust after leveling up (because I'm not ready to read through all the html to figure that out, and I'm sure I haven't accounted for all things). Most times, I run this and it works fine, but on occasion some values drop out. It's not consistent what either. I'll note that the script outputs what has changed and I have confirmed that when something doesn't output a change the attributes actually do not reflect the change either. Fairly often, both surge-value and surges don't change (or output). Sometimes just one in the middle will not change. If one does not change, and other values are dependent on it, re-running the script will often run with old values causing error. For the record, the # signs, although not elegant, are intentional to modify an attribute max rather than the attribute itself. As an update, while doing some additional trial and error I watched the output say it changed hp-bloodied, but hp-bloodied actually remained unchanged.&nbsp; Edit: Since the posting of this I have not been able to reproduce the issue of one disappearing out of the middle and it continuing on normally, but I have on multiple times lost the three changes after hp max, as well as periodically losing just the last two or just the last one.
As an update (and a self-bump): I changed my code to this so that it wouldn't be accessing any changed variables in the same macro. !attrib ac-ability|Math.floor(((@{dexterity}+@{intelligence}+Math.abs(@{dexterity}-@{intelligence}))/2-10)/2)*@{armor-is-light} !attrib fort-ability|Math.floor(((@{strength}+@{constitution}+Math.abs(@{strength}-@{constitution}))/2-10)/2) !attrib ref-ability|Math.floor(((@{dexterity}+@{intelligence}+Math.abs(@{dexterity}-@{intelligence}))/2-10)/2) !attrib will-ability|Math.floor(((@{wisdom}+@{charisma}+Math.abs(@{wisdom}-@{charisma}))/2-10)/2) !attrib hp|#(@{base-hp}+@{hp-feats}+@{hp-per-level}*(@{level}-1)+@{constitution}) !attrib hp-bloodied|Math.floor((@{base-hp}+@{hp-feats}+@{hp-per-level}*(@{level}-1)+@{constitution})/2) !attrib surge-value|(Math.floor((Math.floor((@{base-hp}+@{hp-feats}+@{hp-per-level}*(@{level}-1)+@{constitution})/2))/2)+@{surge-value-bonus}) !attrib surges|#(Math.floor((@{constitution}-10)/2)+@{base-surge-num}) And while executing this I was able to watch as *just* bloodied didn't get changed once. So I think the api must just be choking on itself trying to process everything at once? Is there no script or anything that can change how macros are evaluated? If not I suppose my next best option would be to make a script that calls the other script. I guess I'll get to work on that, but if anyone knows of anything, please do inform me!
Excend said: Is there no script or anything that can change how macros are evaluated? My strong recommendation is switching to ChatSetAttr : !setattr {{ --charid @{character_id} --ac-ability|[[ floor(({@{dexterity}, @{intelligence}}kh1 - 10) / 2) * @{armor-is-light} ]] --fort-ability|[[ floor(({@{strength}, @{constitution}}kh1 - 10) / 2) ]] --ref-ability|[[ floor(({@{dexterity}, @{intelligence}}kh1 - 10) / 2) ]] --will-ability|[[ floor(({@{wisdom}, @{charisma}}kh1 - 10) / 2) ]] --hp||[[ @{base-hp} + @{hp-feats} + @{hp-per-level} * (@{level} - 1) + @{constitution} ]] --hp-bloodied|[[ floor((@{base-hp} + @{hp-feats} + @{hp-per-level} * (@{level} - 1) + @{constitution}) / 2) ]] --surge-value|[[ floor((floor((@{base-hp} + @{hp-feats} + @{hp-per-level} * (@{level} - 1) + @{constitution}) / 2)) / 2) + @{surge-value-bonus} ]] --surges||[[ floor((@{constitution} - 10) / 2) + @{base-surge-num} ]] }}
This is a vastly superior script, so I thank you for bringing it to light. It still seems bizarre to me to be unable to determine the order in which macro lines are executed. In terms of writing a script to do so, is there a way to pass on a selected token? In example, if I want my call to my script to be: !ordered-command "!token-mod --set junk" "!setattr {{junk}}" "/desc @{selected|character_name} does junk" It appears that if I try sendChat, no target is selected, so is there some way I can make a blanket script for calling other scripts with selected targets through chat? Or will I need to make a script that calls the other functions of scripts on a case by case basis as needed? Thanks again!
1480381432

Edited 1480381748
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Your initial call of !ordered-command would have the selected token in the msg.selected. You could setup the script to replace some keyword with the token/character id, name, or something else. So your syntax would look like: !ordered-command __!token-mod ^CHARID --set Junk __!setattr ^CHARID {{junk}} __/desc ^CHARNAME does junk You'd do something like: var OrderedCommand = OrderedCommand || (function() { &nbsp; &nbsp; 'use strict'; &nbsp; &nbsp; var version = '0.01', &nbsp; &nbsp; &nbsp; &nbsp; lastUpdate = 1480381349, &nbsp; &nbsp; &nbsp; &nbsp; schemaVersion = 0.01, &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; checkInstall = function() { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log('-=&gt; OrderedCommand v'+version+' &lt;=- &nbsp;['+(new Date(lastUpdate*1000))+']'); &nbsp; &nbsp; &nbsp; &nbsp; if( ! _.has(state,'OrderedCommand') || state.OrderedCommand.version !== schemaVersion) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log(' &nbsp;&gt; Updating Schema to v'+schemaVersion+' &lt;'); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state.OrderedCommand = { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; version: schemaVersion }; }; }, &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; HandleInput = function(msg_orig) { &nbsp; &nbsp; &nbsp; &nbsp; var msg = _.clone(msg_orig), args, selected = { &nbsp; &nbsp;charid:[], &nbsp; &nbsp;charname:[], &nbsp; &nbsp;tokenid:[] }; &nbsp; &nbsp; &nbsp; &nbsp; if (msg.type !== 'api' || !playerIsGM(msg.playerid)){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; if(_.has(msg,'inlinerolls')){//calculates inline rolls 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(); } &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; args = msg.content.split(/\s+__/);//splits the message contents into discrete arguments switch(args[0]) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!ordered-command': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _.each(msg.selected,(s)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; selected.charid.push(s.get('represents')); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; selected.charname.push(getObj('character',s.get('represents')).id); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; selected.tokenid.push(s.id); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _.each(_.rest(args,1),(a)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a.replace('^CHARID',selected.charid.join(' ')).replace('^CHARNAME',selected.charname.join(' ')).replace('^TOKENID',selected.tokenid.join(' ')); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat(msg.who,a); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; } }, &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; RegisterEventHandlers = function() { &nbsp; &nbsp; &nbsp; &nbsp; on('chat:message', HandleInput); &nbsp; &nbsp; }; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; return { &nbsp; &nbsp; &nbsp; &nbsp; CheckInstall: checkInstall, &nbsp; &nbsp; RegisterEventHandlers: RegisterEventHandlers }; &nbsp; &nbsp;&nbsp; }()); on("ready",function(){ &nbsp; &nbsp; 'use strict'; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; OrderedCommand.CheckInstall(); &nbsp; &nbsp; OrderedCommand.RegisterEventHandlers(); }); That's untested, but should at least give some ideas. Also, be careful what you call with this. Some scripts have playerid gating that will not properly respond to an API generated message sending their command syntax. May have to look at adding this functionality into my customizable roll listeners script now that you've got me thinking about it.
Hmm, I hadn't thought about using replace, I was thinking I would have to add a target argument for each script differently. That would certainly cover the majority of scripts. And I suppose any script that I find and use that doesn't already have an additional argument for a target, can be easily modified to include one. I'll work with this, thank you!