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

[Script] OnMyTurn -- Activate an ability in the context of a token when it's turn occurs.

I'm looking for an API that will run a token's ability macro at the beginning of each turn. It can be via position of the macro or search for the name of the macro. Is there something like this that exists already?
1532271322
The Aaron
Pro
API Scripter
Hmmm.  There are a few problems that would need to be surmounted to get that working, primarily attribute selectors that don't make sense in the API (@{target} and @{selected}) and correcting the context for attribute references that are assumed to be on the character (@{hp}, @{name}, etc).  There's also the matter of who is considered to have issued the ability.  When a player runs an ability or macro, it comes from that player.  When the API does it, it comes from the API.  That usually doesn't matter, but if you're calling API commands in that ability or macro, they won't know who did it and might not work correctly or have visible output. What problem are you trying to solve with this?  There might be an easier way. 
Just for fun, I have a few characters who have very short theme songs that I'd like to play when their turn comes. It would just need to run the roll20AM commands to play them :)
1532334710
Ziechael
Forum Champion
Sheet Author
API Scripter
Sounds like a fun addition to TurnMarker to me... could store the track name in the token GM notes or as an attribute on the sheet and trigger a track when the turn marker moves on? (Of course Aaron will come up with something much more complex and fully featured...)
1532352771
The Aaron
Pro
API Scripter
I think I'd do it separately from TurnMarker.  Probably a stand alone script or addition to Roll20 Audio Master
I think a stand alone script would be most beneficial as this would be only one of it's potential uses. A stand alone script could do any number of things such as alert players of their turn in chat or spit out an ability button list in chat for quick turn by turn actions :) just a thought!
1532369064

Edited 1535898706
The Aaron
Pro
API Scripter
Ok.  HoneyBadger shamed me into writing one: Just create an ability on a character and name that ability " OnMyTurn " and put stuff in it.  It will get executed when a token representing that character's turn comes up, with the following caveats: 1) Any bare references to an attribute will be resolved on the character. eg: @{name}, @{hp|max} 2) Any reference to @{selected} or @{target} will be replaced with the token whose turn it is, falling back to the character. eg: @{selected|token_id}, @{target|hp}, @{target|Person|bar1|max} 3) The API will be invoked by the API, so scripts relying on who called them to give feedback will be sending that output to the "API" character. Script: on('ready', () => { const resolver = (token,character) => (text) => { const attrRegExp = /@{(?:([^|}]*)|(?:(selected)|(target)(?:\|([^|}]*))?)\|([^|}]*))(?:\|(max|current))?}/gm; const attrResolver = (full, name, selected, target, label, name2, type) => { let simpleToken = JSON.parse(JSON.stringify(token)); let charName = character.get('name'); type = ['current','max'].includes(type) ? type : 'current'; const getAttr = (n, t) => ( findObjs({type: 'attribute', name:n, characterid: character.id})[0] || {get:()=>getAttrByName(character.id,n,t)} ).get(t); const getFromChar = (n,t) => { if('name'===n){ return charName; } return getAttr(n,t); }; const getProp = (n, t) => { switch(n){ case 'token_name': return simpleToken.name; case 'character_name': return charName; case 'bar1': case 'bar2': case 'bar3': return simpleToken[`${n}_${'max'===t ? 'max' : 'value'}`]; } return getFromChar(n,t); }; if(name){ return getFromChar(name,type); } return getProp(name2,type); }; return text.replace(attrRegExp, attrResolver); }; const checkOnMyTurn = (obj,prev) => { let to=JSON.parse(obj.get('turnorder')||'[]'); let toPrev=JSON.parse(prev.turnorder||'[]'); if(to.length && to[0].id!=='-1' && to[0].id !== (toPrev[0]||{}).id){ let token = getObj('graphic',to[0].id); if(token && token.get('represents')){ let character = getObj('character',token.get('represents')); let ability = findObjs({ name: 'OnMyTurn', characterid: character.id }, {caseinsensitive: true})[0]; if(ability){ let content = resolver(token,character)(ability.get('action')).replace(/\[\[\s+/g,'[['); try { sendChat(character.get('name'),content); } catch(e){ log(`OnMyTurn: ERROR PARSING: ${content}`); log(`OnMyTurn: ERROR: ${e}`); } } } } }; on( 'change:campaign:turnorder', (obj,prev)=>setTimeout(()=>checkOnMyTurn(Campaign(),prev),1000) ); on('chat:message', (msg) => { if('api'===msg.type && /^!eot\b/.test(msg.content)){ setTimeout(()=>checkOnMyTurn(Campaign(),{turnorder:JSON.stringify([{id:-1}])}),1000); } }); }); Edit: Added support for !eot Edit: Added fixing of inline roll bug, exception logging for errors.
You're a wizard and a saint :D
1532392569
The Aaron
Pro
API Scripter
=D
I hate to ask for support when you've made this amazing API for me and everyone but it's throwing some exceptions :*( I'll try looking at debugging it but I'm not very familiar with Roll20's API system yet. Reproduction steps: 1) Add OnMyTurn ability to a character 2) Add character to turn order Actual: Exception in Roll20 API page Expected: Turn orders are sorted and OnMyTurn script is run on that character's turn. Exception: TypeError: Cannot read property 'id' of undefined TypeError: Cannot read property 'id' of undefined at checkOnMyTurn (apiscript.js:9024:66) at Timeout.setTimeout [as _onTimeout] (apiscript.js:9079:36) at ontimeout (timers.js:386:14) at tryOnTimeout (timers.js:250:5) at Timer.listOnTimeout (timers.js:214:5) Workaround currently is to restart the API after each combat initiative is rolled.
1532517476
The Aaron
Pro
API Scripter
No problem. I always want any script I write to work, so definitely let me know if it has a problem. I’ll see if I can reproduce this, but I already see the error thanks to the callstack you posted: if(to.length && to[0].id!=='-1' && to[0].id !== toPrev[0].id ){ changing to this: if(to.length && to[0].id!=='-1' && to[0].id !== (toPrev[0]||{}).id ){ should fix it.   I’ll test and update the above code.
1532518422
The Aaron
Pro
API Scripter
Yup, that fixed it.  I posted a new copy of OnMyTurn up above.  It's just slightly refactored in case anyone else needs to resolve the contents of an ability from the API. =D
The Aaron, you are amazing. I have yet to try this out in my game, but if it works as advertised (ANYthing can go in the ability ?) you may have just changed my world, once again! Thank you!
1532555601
The Aaron
Pro
API Scripter
HAHAHA.. Well, I don't know about ANYTHING .  Ability and Macro references won't work.
Ah. So, not anything. I can't, in other words, just automatically have each monster make an attack (using a macro that references an ability) when its it's turn. Gonna have to lower my rating of you to infinity minus one. Tsk tsk. Let me know when you come up with something good. -Phnord.
1532584953

Edited 1540154999
The Aaron
Pro
API Scripter
Maybe then I can complete my nickel collection!
Works great again, Aaron! :D There's a minor bug where OnMyTurn runs twice in some cases when more than one character has OnMyTurn abilities. Not a huge deal :) Phnord Prephect, there's a workaround in that you can enter the template that you want the specific thing to do, bypassing the character sheet reference altogether. I use it for my main characters and it seems to function perfectly fine. Takes some work, though.
1532628211
The Aaron
Pro
API Scripter
Nice!  I'll try and reproduce that double run case.  Was also thinking I might be able to support ability and macro references with some recursion... 
1532637293

Edited 1532638369
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
That would be really cool, since any character could have a standardly-named referenced ability of stuff that should happen on their turn. A monster could post GM Notes at the beginning of its turn, or run a chat macro of commonly used abilities, or play a sound, etc., with each creature having its own custom suite. Grammatical confusion inspired post...
1532637700
The Aaron
Pro
API Scripter
Yup. =D I mean, you can already do that with an ability named OnMyTurn with this script, but adding macro support would be nice for when you have a bunch of shared behavior you want to manipulate in one place.
1532638320

Edited 1532638699
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Haha! I was grammatically misreading: create an ability on a character named "OnMyTurn" to be  [create an ability] on a [character named "OnMyTurn"] instead of  [create an ability on a character]   [named "OnMyTurn"] That makes much more sense. So I was reading it as simply a macro that would occur at the beginning of every turn, but would apply to whomever's turn it happened to be.
1532639682
The Aaron
Pro
API Scripter
Ah!  Edited for clarity: Just create an ability on a character and name that ability " OnMyTurn " and put stuff in it
This is awesome, as always, Aaron.  If I may I suggest a possibly obvious extension?  Can we get some code to execute a second ability, OnMyTurnEnd ?  I frequently use a TurnStart macro that contains things like what spell I'm concentrating on, and a reset button for my AC (for if I've cast shield since last turn) that I have to remember to click at the start of my turn, but also a TurnEnd macro that uses ChatSetAttr to decrement a series of custom attributes, and then prints a PowerCard telling me how many turns remain of anything with a value greater than 10 to keep track of the various buffs I use.  Seems like this would be a natural extension of this.  ^^
1534169405
The Aaron
Pro
API Scripter
Yes, though that's not as simple as it sounds.  Since event notifications occur on the trailing edge of an event, whatever the event does has already been done.  In the case of OnMyTurn, this is fine as the event ( change:campaign:turnorder  or a chat:message  of "!eot") sets up the current turn's token as the first token in the turn order, so there is a definitive "who's turn is it."  There is not a definitive "who's turn was it" though, as various scripts or other turn order entries may exist which are manipulating these things.  2 examples: Custom Turn Orders and Turn Markers provide most of the problems.  It can be done, it just isn't as easy.  Likely, I'll instead queue up the event to be executed when a turn ends at the point that a token gets it's turn, and then trigger it on the next turn order change.  That will require some work in the state (so that restarts or a week or two delay between beginning and ending a turn work correctly) and some slightly different handling of checkOnMyTurn.   Still a good suggestion and I'll try to get it out at some point... (after a few other promises are kept! =D )
Hey Aaron this is awesome! I was thinking of using this instead of my "Start Turn" button I have. The function of the start turn is to track (or more accurately clear) the usage of bonus and reactions on the previous turn. I do this with your token mod, that adds a Red circle for reactions and a Blue for bonus. So the Start turn simply removes them and for fun gives a little emote /me gets ready... My question, Can this be used to run my token mod command? or even the full macro? !token-mod --set statusmarkers|-blue|-red|-yellow /em gets ready!!! I tried adding #Start to the attribute and then just the Token-mod command.. both broke the script. Am I doing it wrong? Or is this not supported by the script?
So yeah in the last 9 minutes I learned how to READ!!! So I decided to make it an ABILITY not and Attribute like you said in the 1st place! LOL Works fine now just had to add the character ID to the token mod command.
1534176610
The Aaron
Pro
API Scripter
Great!
The Aaron said: Yes, though that's not as simple as it sounds.  Since event notifications occur on the trailing edge of an event, whatever the event does has already been done.  In the case of OnMyTurn, this is fine as the event ( change:campaign:turnorder  or a chat:message  of "!eot") sets up the current turn's token as the first token in the turn order, so there is a definitive "who's turn is it."  There is not a definitive "who's turn was it" though, as various scripts or other turn order entries may exist which are manipulating these things.  2 examples: Custom Turn Orders and Turn Markers provide most of the problems.  It can be done, it just isn't as easy.  Likely, I'll instead queue up the event to be executed when a turn ends at the point that a token gets it's turn, and then trigger it on the next turn order change.  That will require some work in the state (so that restarts or a week or two delay between beginning and ending a turn work correctly) and some slightly different handling of checkOnMyTurn.   Still a good suggestion and I'll try to get it out at some point... (after a few other promises are kept! =D ) Speculation: Couldn't you just check the turn order at the end of !eot, after everything else is done, and read the last name off the list?  ^^ Regardless: Awesome, I await hopefully.  ^^
1534347245
The Aaron
Pro
API Scripter
Not really.  The last turn might be a Custom Turn Entry that represents a status like concentration on a spell, or might be some hidden tokens that were automatically skipped, etc.  The more I think about it, the more I think what I outlined would be pretty easy and super useful. =D
Im puzzled... I broke this, but only kinda? LOL So I run this on 2 games.. one it works fine, the other it throws an error when the turn order advances. TypeError: Cannot read property 'get' of undefined TypeError: Cannot read property 'get' of undefined at handleInput (apiscript.js:25843:36) at eval (eval at <anonymous> (/home/node/d20-api-server/api.js:151:1), <anonymous>:65:16) at Object.publish (eval at <anonymous> (/home/node/d20-api-server/api.js:151:1), <anonymous>:70:8) at /home/node/d20-api-server/api.js:1634:12 at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:560 at hc (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:39:147) at Kd (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:546) at Id.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:489) at Zd.Ld.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:94:425) at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:111:461 the abilities are the same on both, ive used it on each character (so I dont think its character specific), and ive made sure its the active page (as in the Player ribbon is on that page). This is what I have as the ability in both campaigns. !roll20AM --audio,play,nomenu|@{selected|click} !roll20AM --audio,play,fade,in,delay:15,nomenu|Timer !token-mod --ids @{selected|character_id} --set statusmarkers|-blue|-red|-yellow /em gets ready!!!
1535399796
The Aaron
Pro
API Scripter
So, I'm going to guess that's an issue in Roll20 Audio Master?  Try removing each line from the macro and advancing the turn.  When you are able to do it without the crash, that's the script causing the issue.  The issue is happening in a handleInput() method, knowing which script's handleInput() will make tracking down the reason possible.
Copy that... testing now
OK so... I re-copied the script, and disabled !Emas. It's working almost.. The /em works and !roll20AM is working, but the Tokenmod command isn't removing the statuses.
yeah I think its a conflict with !emas, I turned it back on and the API crashed again. Not a big deal im not actually using !emas. Not sure why it works perfectly on one game and not the other. Im checking the "working campaign" vs this broken one to see if I have the scripts the same.
1535432733

Edited 1535432884
OK so I realized that I didnt allow players to use -ids, so its working 100% again. But still some kind of conflict with !emas. **Dumps a trash-bag's worth of paper work in Aaron's "To-Do" box** If you could get that done by 3pm, that would be GREEEEAAATTT...Oh and you're going to have to come in on Saturday... yeaaahh...
1535460847
The Aaron
Pro
API Scripter
As long as there are no TPS reports... =D
1535898758
The Aaron
Pro
API Scripter
I just updated the script above to fix instances of this bug:&nbsp; <a href="https://app.roll20.net/forum/post/2217884/api-regressions-for-inline-dice-expression-in-the-sendchat-api-function" rel="nofollow">https://app.roll20.net/forum/post/2217884/api-regressions-for-inline-dice-expression-in-the-sendchat-api-function</a> And to Log if there are other errors.
&lt;3