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

Can tokens be added to the Turn Tracker with API?

1631922351

Edited 1631923761
Hi everyone! So, I've created a campaign where all checks (skill checks, attribute checks, etc.) are handled with the draw of a pseudo Tarot deck generated via API from hidden chat commands (!tarot, etc.). Works pretty well now. But I'd like to get the result of an Initiative check (an integer labelled "nTotal") into the Turn Tracker without generating a die roll (die rolls leave tons of unattractive junk about the roll in the chat area). Is there any was this can be done strictly with API? Is the Turn Tracker an object that can be accessed? Any kind of help of suggestions on how to handle this in an attractive and functional way would be GREAT! I mean, I can run things without the tracker... but, you know. The thing is SOOOO handy. Thanks in advance for your help! // Final total resulting from API generated check var nTotal; // Character ID var sID; // Character Name var sCNm;
1631923868
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Yup. The Turn Order is a JSON string in the Campaign object.You can stringify it, and then edit it how you will. Documentation is here .
1631924571
The Aaron
Roll20 Production Team
API Scripter
Here's a function that adds tokens to the TurnOrder: const addTokenTurn = (id, pr) => setTurnArray([...getTurnArray(), {id,pr}]); You'd use it like this: // got a token somehow let token = [something]; addTokenTurn(token.id, someNumber); Here's a bunch of functions I wrote for dealing with the TurnOrder: /* eslint-disable no-unused-vars */ const getTurnArray = () => ( '' === Campaign().get('turnorder') ? [] : JSON.parse(Campaign().get('turnorder'))); const getTurnArrayFromPrev = (prev) => ( '' === prev.turnorder ? [] : JSON.parse(prev.turnorder)); const setTurnArray = (ta) => Campaign().set({turnorder: JSON.stringify(ta)}); const addTokenTurn = (id, pr) => setTurnArray([...getTurnArray(), {id,pr}]); const addCustomTurn = (custom, pr) => setTurnArray([...getTurnArray(), {id:"-1",custom,pr}]); const removeTokenTurn = (tid) => setTurnArray(getTurnArray().filter( (to) => to.id !== tid)); const removeCustomTurn = (custom) => setTurnArray(getTurnArray().filter( (to) => to.custom !== custom)); const clearTurnOrder = () => Campaign().set({turnorder:'[]'}); const packTo = (to) => [{id:'HEADER',pr:Number.MAX_SAFE_INTEGER},...to].reduce((m,t)=>{ if('-1'===t.id){ m[m.length-1].packed=[...(m[m.length-1].packed || []), t]; return m; } return [...m,t]; },[]); const unpackTo = (pTo) => pTo.reduce((m,t)=>{ let packed = t.packed||[]; delete t.packed; if('HEADER' === t.id){ return [...packed,...m]; } return [...m,t,...packed]; },[]); const sorter_asc = (a, b) => ('-1' === a.id || '-1' === b.id) ? 0 : a.pr - b.pr; const sorter_desc = (a, b) => ('-1' === a.id || '-1' === b.id) ? 0 : b.pr - a.pr; const sortTurnOrder = (sortBy = sorter_desc) => Campaign().set({turnorder: JSON.stringify(unpackTo(packTo(getTurnArray()).sort(sortBy)))}); /* eslint-enable no-unused-vars */
Okay. Stupid question time (I know, already done that). If I know the character's ID, I can get the token's ID using the " _defaulttoken " property of the character object, right?  Something like: var sTokenID = oCharacter. get("_defaulttoken") ? The Aaron said: Here's a function that adds tokens to the TurnOrder: const addTokenTurn = (id, pr) => setTurnArray([...getTurnArray(), {id,pr}]); You'd use it like this: // got a token somehow let token = [something]; addTokenTurn(token.id, someNumber);
1631934790

Edited 1647524633
The Aaron
Roll20 Production Team
API Scripter
Nope, you need to find the token on the current page that represents the character.  It's not super straight forward, something like (given somePlayerId and someNumber): const getPageForPlayer = (playerid) => { let player = getObj('player',playerid); if(playerIsGM(playerid)){ return player.get('lastpage') || Campaign().get('playerpageid'); } let psp = Campaign().get('playerspecificpages'); if(psp[playerid]){ return psp[playerid]; } return Campaign().get('playerpageid'); }; let pageid = getPageForPlayer( somePlayerId ); let tokens = findObjs({ type: 'graphic', represents: character.id, pageid: pageid }); if(tokens[0]){ addTokenTurn(tokens[0].id, someNumber ); }
1631935310

Edited 1632001904
Nothing's ever simple, is it?  lol  Thanks for the code. MUCH appreciated.
1631997537

Edited 1632001868
Hey Aaron. So, having some issues using your function. I managed to figure out how to get the token ID and identify the token object, but I'm getting an error now that says:  ReferenceError: setTurnArray is not defined  (full error included below as a snapshot). Here is my nasty script (yes, I suck at coding).  I set your function at the top const addTokenTurn = (id, pr) => setTurnArray([...getTurnArray(), {id,pr}]); Then my code listens to chat messages for the leading code I want: ////////////////////////////////////////////////////////////////// // ADD TO TURN TRACKER if(msg.content.indexOf("!att") !== -1){ // Get initial variables var sMsg = msg.content.replace("!att!", ""); var aMsg = sMsg.split("!"); var sTID = aMsg[0].trim(); var oTok = getObj("graphic", sTID); addTokenTurn(oTok.sTID, 10); } So what am I doing wrong? Full error snapshot: I feel I should add that the snippet above is contained within a chat message function, like so: on("chat:message", function(msg) { // Your function // My code snippet } And the chat message coming in would be something like this: !att!-MVlfhXBPJlwkR13PFIz
1632013858
The Aaron
Roll20 Production Team
API Scripter
Whoops, that was my fault. You just need to copy in the getTurnArray and setTurnArray functions from the lower block of code. 
Well, I just went ahead and added the whole block of all your functions. And it's no longer throwing an error. But It's also not doing anything (though the script IS running, I've tested that). Any ideas? Am I getting the token object wrong? Entering it into your function wrong? The Aaron said: Whoops, that was my fault. You just need to copy in the getTurnArray and setTurnArray functions from the lower block of code. 
1632018804
The Aaron
Roll20 Production Team
API Scripter
The problem is oTok doesn't have a property of sTID in this line: addTokenTurn( oTok.sTID , 10); oTok is a Roll20 object, which means it as a property of .id, and a bunch of properties accessible with the .get() function. Give this a try: !att @{target|token_id} With this script: on('ready',()=>{ /* eslint-disable no-unused-vars */ const getTurnArray = () => ( '' === Campaign().get('turnorder') ? [] : JSON.parse(Campaign().get('turnorder'))); const getTurnArrayFromPrev = (prev) => ( '' === prev.turnorder ? [] : JSON.parse(prev.turnorder)); const setTurnArray = (ta) => Campaign().set({turnorder: JSON.stringify(ta)}); const addTokenTurn = (id, pr) => setTurnArray([...getTurnArray(), {id,pr}]); const addCustomTurn = (custom, pr) => setTurnArray([...getTurnArray(), {id:"-1",custom,pr}]); const removeTokenTurn = (tid) => setTurnArray(getTurnArray().filter( (to) => to.id !== tid)); const removeCustomTurn = (custom) => setTurnArray(getTurnArray().filter( (to) => to.custom !== custom)); const clearTurnOrder = () => Campaign().set({turnorder:'[]'}); const packTo = (to) => [{id:'HEADER',pr:Number.MAX_SAFE_INTEGER},...to].reduce((m,t)=>{ if('-1'===t.id){ m[m.length-1].packed=[...(m[m.length-1].packed || []), t]; return m; } return [...m,t]; },[]); const unpackTo = (pTo) => pTo.reduce((m,t)=>{ let packed = t.packed||[]; delete t.packed; if('HEADER' === t.id){ return [...packed,...m]; } return [...m,t,...packed]; },[]); const sorter_asc = (a, b) => ('-1' === a.id || '-1' === b.id) ? 0 : a.pr - b.pr; const sorter_desc = (a, b) => ('-1' === a.id || '-1' === b.id) ? 0 : b.pr - a.pr; const sortTurnOrder = (sortBy = sorter_desc) => Campaign().set({turnorder: JSON.stringify(unpackTo(packTo(getTurnArray()).sort(sortBy)))}); /* eslint-enable no-unused-vars */ on('chat:message',msg=>{ if('api'===msg.type && /^!att(\b\s|$)/i.test(msg.content) && playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); ////////////////////////////////////////////////////////////////// // ADD TO TURN TRACKER // Get initial variables let args = msg.content.split(/\s+/).slice(1); args.forEach(id=>{ let oTok = getObj('graphic', id); if(oTok){ addTokenTurn(oTok.id,10); } }); } }); });
1632020601

Edited 1632021111
That works AWESOMELY! It also seems to work with  !att @{selected|token_id},  So, PERFECT! Thank you SOOOO much, Aaron for taking the time to show me that and put that script together. The Aaron said: The problem is oTok doesn't have a property of sTID in this line: addTokenTurn( oTok.sTID , 10); oTok is a Roll20 object, which means it as a property of .id, and a bunch of properties accessible with the .get() function. Give this a try: !att @{target|token_id}
1632021035

Edited 1632024478
The Aaron
Roll20 Production Team
API Scripter
Oh, it totally can, check this version (additions highlighted).  It will add all selected tokens to the turn order. on('ready',()=>{ /* eslint-disable no-unused-vars */ const getTurnArray = () => ( '' === Campaign().get('turnorder') ? [] : JSON.parse(Campaign().get('turnorder'))); const getTurnArrayFromPrev = (prev) => ( '' === prev.turnorder ? [] : JSON.parse(prev.turnorder)); const setTurnArray = (ta) => Campaign().set({turnorder: JSON.stringify(ta)}); const addTokenTurn = (id, pr) => setTurnArray([...getTurnArray(), {id,pr}]); const addCustomTurn = (custom, pr) => setTurnArray([...getTurnArray(), {id:"-1",custom,pr}]); const removeTokenTurn = (tid) => setTurnArray(getTurnArray().filter( (to) => to.id !== tid)); const removeCustomTurn = (custom) => setTurnArray(getTurnArray().filter( (to) => to.custom !== custom)); const clearTurnOrder = () => Campaign().set({turnorder:'[]'}); const packTo = (to) => [{id:'HEADER',pr:Number.MAX_SAFE_INTEGER},...to].reduce((m,t)=>{ if('-1'===t.id){ m[m.length-1].packed=[...(m[m.length-1].packed || []), t]; return m; } return [...m,t]; },[]); const unpackTo = (pTo) => pTo.reduce((m,t)=>{ let packed = t.packed||[]; delete t.packed; if('HEADER' === t.id){ return [...packed,...m]; } return [...m,t,...packed]; },[]); const sorter_asc = (a, b) => ('-1' === a.id || '-1' === b.id) ? 0 : a.pr - b.pr; const sorter_desc = (a, b) => ('-1' === a.id || '-1' === b.id) ? 0 : b.pr - a.pr; const sortTurnOrder = (sortBy = sorter_desc) => Campaign().set({turnorder: JSON.stringify(unpackTo(packTo(getTurnArray()).sort(sortBy)))}); /* eslint-enable no-unused-vars */ on('chat:message',msg=>{ if('api'===msg.type && /^!att(\b\s|$)/i.test(msg.content) && playerIsGM(msg.playerid)){ let tokens = (msg.selected || []) .map(o=>getObj('graphic',o._id)) .filter(g=>undefined !== g) ; ////////////////////////////////////////////////////////////////// // ADD TO TURN TRACKER // Get initial variables let args = msg.content.split(/\s+/).slice(1); args.forEach(id=>{ let oTok = getObj('graphic', id); if(oTok){ addTokenTurn(oTok.id,10); } }); tokens.forEach(t=>addTokenTurn(t.id,10)); } }); });
Sweet! I had just edited my response because I tested your previous script and it WORKED with selected. lol But being able to add ALL selected is even better. You the MAN, Aaron! The Aaron said: Oh, it totally can, check this version (additions highlighted).  It will add all selected tokens to the turn order.
So, I just put in the new script and got the following error. It's no big. I can use your previous script. Just thought you should know. The Aaron said: Oh, it totally can, check this version (additions highlighted).  It will add all selected tokens to the turn order.
1632022693
timmaugh
Pro
API Scripter
Think that's just this line: tokens.forEach(t=>addTokenTurn(t.id,10); It needs another closing parentheses before the semi colon: tokens.forEach(t=>addTokenTurn(t.id,10));
1632024455

Edited 1632024507
The Aaron
Roll20 Production Team
API Scripter
Whoops, thanks for catching that Tim!   I edited my post to fix the script. =D
Thanks again guys! This is awesome!
1632071824
The Aaron
Roll20 Production Team
API Scripter
If you want more complicated interactions, I have the GroupInitiative script that does this with character sheet tie-ins and die rolling. =D
Feel free to shoot that my way! In this instance (since I am replacing die rolls with a pseudo Tarot draw), that would be less useful. But for my regular D&D games it would likely be awesome! The Aaron said: If you want more complicated interactions, I have the GroupInitiative script that does this with character sheet tie-ins and die rolling. =D
1632177480
The Aaron
Roll20 Production Team
API Scripter
This is the most recent post about it:&nbsp; <a href="https://app.roll20.net/forum/post/7921574/script-update-groupinitiative-now-with-dice-modifiers-for-the-initial-roll/?pagenum=1" rel="nofollow">https://app.roll20.net/forum/post/7921574/script-update-groupinitiative-now-with-dice-modifiers-for-the-initial-roll/?pagenum=1</a> It's in the 1-click scripts.
Cool! Thanks, Aaron! The Aaron said: This is the most recent post about it:&nbsp; <a href="https://app.roll20.net/forum/post/7921574/script-update-groupinitiative-now-with-dice-modifiers-for-the-initial-roll/?pagenum=1" rel="nofollow">https://app.roll20.net/forum/post/7921574/script-update-groupinitiative-now-with-dice-modifiers-for-the-initial-roll/?pagenum=1</a> It's in the 1-click scripts.