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 Update] TokenMod -- An interface to adjusting properties of a token from a macro or the chat area.

1578429412

Edited 1578429440
Thank you for the responses Keith and Aaron. I should have been more clear in my question. I am looking for a way to reveal the text overlay on the HP bar so that players can see that an npc has 93/93 hitpoints for instance, rather than the just the blank bar. Using the macros above seems to only reveal the bar. In the token options, this is enabled by clicking "Visible to everyone" under the Text Overlay as shown in the attached screenshot. 
1578438301
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Unfortunately, despite numerous requests, some of the newer features of tokens have not been made available to the API. :(
Unfortunate to hear, but thanks for letting me know!
1578450600
The Aaron
Roll20 Production Team
API Scripter
We keep asking though, so keep asking!
Is there a way to append text to a token's nameplate? For instance if the token currently reads: "John", could I create a macro that appends its AC so that it reads: "John / 17 AC"?
1580506384
The Aaron
Roll20 Production Team
API Scripter
Yup !token-mod --set name|"@{selected|token_name} / @{selected|ac} AC"
Excellent, thanks!
FYI, note that if the character's AC changes, it won't update the nameplate until you run that command again.
1580569152

Edited 1580569292
The Aaron said: Yup !token-mod --set name|"@{selected|token_name} / @{selected|ac} AC" Will that upset dependencies?  Such as @{[token_name]|[macro]} in other Macros that may be on the character's sheet? For example %{John / AC 17|This-Macro} - the name Changes to John / AC 19 because his shield was added and the name change macro was run. Now %{John / AC 17|This-Macro} will not run, because it is looking for %{John / AC 19|This-Macro}, which doesn't exist - per the error that appears. I know that's where the dynamic "Selected" or "Target" would come into play ( %{selected|This-Macro} ) but I always wondered if having anything change the name, where macros were dependent on the token_name, would ever be a wise idea.
1580573719
The Aaron
Roll20 Production Team
API Scripter
When you use a name as the first part of an attribute reference, it's the character's name, not the token's. It shouldn't mess with macros that rely on the name.  That said, if you make abilities on a character, the character is assumed so you don't need the name at all. 
1580597194

Edited 1580597287
The Aaron said: When you use a name as the first part of an attribute reference, it's the character's name, not the token's. It shouldn't mess with macros that rely on the name.  That said, if you make abilities on a character, the character is assumed so you don't need the name at all.  gotcha
1580796482
Phillip G.
Plus
Sheet Author
I have what I feel is a sweet macro for use with the Token-mod API. I'd like to share it, but I can't recall how to put the code in the grey box thingy.
1580797291

Edited 1580797509
Phillip G.
Plus
Sheet Author
Not sure if anyone has tried to do this before, but I wanted to have a macro that would allow me to select a Condition and then place a pre-selected status marker on the token for that Condition. This would work great for games where there are a lot of conditions (Pathfinder 2 I'm looking at you.) Anyway, with the custom status markers coming out I just knew I had to put it together properly. The Aaron suggest I use Token-mod after I had some issues with the out-dated Marking Conditions API script. I started getting frustrated (Token-mod is a bit more complex), but then I had a break through. Anyway, below is the macro, it requires that you have Token-mod API loaded and that you have custom status markers set Czepeku 80 Status Effect Icons loaded (just the big images, leave the smalls on your own system). You should be able to make it work with any custom status markers by changing the options in the queries. The first tier query allows you to choose from setting a status, changing a status value, removing a status, or removing all statuses. Subsequent query tiers present the conditions and possibly a value depending on the condition. This macro was specifically designed for Pathfinder (2nd ed.) but you could tweak it for any system. Let me know what you think. ?{Choose an action|Set status marker,!token-mod --set statusmarkers|?{Which condition| Blinded,Blind| Clumsy, Drunk:?{What value|1} | Concealed,Hidden | Confused,Confused | Controlled,Enrapture | Dazzled,Polymorph1 | Deafened,Deaf | Doomed,Doom:?{What value|1} | Drained,Blee ding: ? {What value|1} | Dying,Unconscious:?{What value|1} | Enfeebled,AttackDown:?{What value|1} | Fascinated,Charmed | Fatigued,DefenseDown | Flat-footed,Confusion2 | Frightened,Fear:?{What value|1} | Grabbed,Grapple | Hidden,Hidden | Immobilized, MovementDisabled | Invisible,Invisible | Paralyzed,Disabled | Persistent Damage,?{Choose energy type | Bleed,Bleed1:?{What value|1} &#124 Acid,Acid:?{What value|1 } | Cold,Freeze:?{What value|1} | Fire,Burn:?{What value |1} | Force,Knockdown:?{What value|1} | Lightning, Shock:?{What value|1} | Necrotic,Undead:?{What value|1} | Poison,Poison1:?{What value|1} | Psychic,Confusion1:?{What value|1& amp;#125; | Radiant,Blessed:?{What value|1} | Thunder,Debuff2:?{What value |1} } | Petrified,Petrify | Prone,Prone | Quickened,Haste | Restrained,Root | Sickened,Poisoned:?{What value|1} | Slowed,Slow:?{What value|1 } | Stunned,Stun:?{What value|1} | Stupefied,DisabledMagic:?{What value|1} | Unconscious,Unconscious | Wounded,Alert:?{What value|1} | red,red | blue,blue | green,green | brown,brown | purple,purple | pink,pink | yellow,yellow } |Change status marker value,!token-mod --set statusmarkers|?{Which condition| Clumsy,?Drunk:?{What value|} | Doomed,?Doom:?{What value|} | Drained,?Bleeding:?{What value|} | Dying, ?Unconscious:?{What value|} | Enfeebled,?AttackDown:?{What value|} | Frightened, ?Fear:?{What value|} | Sickened,?Poisoned:?{What value|} | Slowed,?Slow:?{What value|} | Stunned,?Stun:?{What value|} | Stupefied,?DisabledMagic:?{What value|} | Wounded,?Alert:?{What value|} } |Remove status marker,!token-mod --set statusmarkers|?{Which condition| Blinded,-Blind| Clumsy,-Drunk | Concealed,-Hidden | Confused, -Confused | Controlled,-Enrapture | Dazzled,-Polymorph1 | Deafened,-Deaf | Doomed,-Doom | Drained,-Bleeding | Dying,-Unconscious | Enfeebled,-AttackDown | Fascinated,-Charmed | Fatigued, -DefenseDown | Flat-footed,-Confusion2 | Frightened,-Fear | Grabbed,-Grapple | Hidden,-Hidden | Immobilized,-MovementDisabled | Invisible,-Invisible | Paralyzed,-Disabled | Persistent Damage,?{Choose energy type | Bleed,-Bleed1 &#124 Acid,-Acid | Cold,-Freeze | Fire,-Burn | Force,-Knockdown | Lightning,-Shock | Necrotic,-Undead | Poison, -Poison1 | Psychic,-Confusion1 | Radiant,-Blessed | Thunder,-Debuff2 } | Petrified,-Petrify | Prone,-Prone | Quickened,-Haste | Restrained,-Root | Sickened,-Poisoned | Slowed,-Slow | Stunned,-Stun | Stupefied,-DisabledMagic | Unconscious,-Unconscious | Wounded ,-Alert | red,-red | blue,-blue | green,-green | brown,-brown | purple,-purple | pink,-pink | yellow,-yellow } |Remove all status markers,!token-mod --set statusmarkers|=dead|-dead } Edit - I had to manually wrap the text so watch you carriage returns when you copy it.
1580815747
The Aaron
Roll20 Production Team
API Scripter
Wow! Glad you got it working. 
1581090164

Edited 1581094506
Hello i would like to make a macro for the SR5 sheet to format my tokens : Bar 1 (stun wound) : current value / Max value Bar 2 (Armor) : current value (this bar must be hidden AND linked) Bar 3 (Physical wound) : current value / Max value I have done this : !token-mod --set bar1_link| bar2_link| bar3_link| bar1| bar2| bar3| !token-mod {{ --set bar1|@{selected|stunwoundstaken} --set bar2_link|armor_total --set bar3|@{selected|physicalwoundstaken} --set name|@{selected|character_name} --off showname --off showplayers_name --on showplayers_bar1 --on showplayers_bar2 --on showplayers_bar3 --on showplayers_aura1 --on showplayers_aura2 --on light_hassight }} But i don't have the max values and the Link to "armor total" attribute do not work I use  "link" with token mod on my D&D campaign and it work nice, but with the Shadowrun5 sheet it doesn't work.
1581094865

Edited 1581094903
!token-mod {{ --set bar1_value|@{selected|stunwoundstaken} bar1_max|@{selected| MAX Value } bar2_value|@{selected|armor_total} bar3_value|@{selected|physicalwoundstaken} bar3_max|@{selected| MAX Value } }}
This is such a time saver, can I ask though..  Is there a way to select multiple tokens and run  !token-mod --set statusmarkers|?red:1-9 To essentially number them each a number?  I'm playing with it but haven't found how to perform a for loop.  If this is already these posts and I've missed it I'm very sorry.  
Have you seen the TokenNameNumber API script? <a href="https://app.roll20.net/forum/post/1273423/script-tokennamenumber-automatic-numbering-of-qualifying-tokens/" rel="nofollow">https://app.roll20.net/forum/post/1273423/script-tokennamenumber-automatic-numbering-of-qualifying-tokens/</a>
1582082153
The Aaron
Roll20 Production Team
API Scripter
Yeah, TNN is is probably a better choice, as Rabulias suggests.&nbsp;
Is there a way to have TokenMod resolve rolls individually for multiple selected tokens? For instance, I have macro that names tokens based on a random table:&nbsp; !token-mod --set name|"[[1t[NPCNames]]]" However if I select multiple tokens and run the macro, it will name them all the same random name. So I'm wondering if there is a method to have them all resolve independently so they will all have random names from the table that are different from each other.
1582135674
The Aaron
Roll20 Production Team
API Scripter
No, because the expansion of most of those things happens before the API even sees the message.&nbsp; If you take the ! off the front and look at what shows up in chat, that's basically what the API gets. There are some things I want to do that could fix that's for inline rolls, but it's not something I've gotten around to yet.&nbsp;
Interesting, thanks for the quick response!
1582916028

Edited 1582916070
Trying to figure out why controlledby is able to allow control, but is there a way to get the character sheet viewable to the journal? !token-mod {{ --set name|'Wildshape: @{selected|token_name}' controlledby|+Sound }} I have a whole bunch of copied druid beast shapes I wanna just drop a token onto the field and run the API to assign.&nbsp; This way I can mass convert their mental stats and everything that needs changed to the druid character's, not effecting the original NPC sheets, with Tokenmod and CSA. Side Note:&nbsp; Why do Druids have to be so difficult/ frustrating when using Roll20?&nbsp; -_- (Rhetorical Question)
1582921321
Dumbhuman
Pro
Marketplace Creator
Wolf Thunderspirit said: Trying to figure out why controlledby is able to allow control, but is there a way to get the character sheet viewable to the journal? !token-mod {{ --set name|'Wildshape: @{selected|token_name}' controlledby|+Sound }} I have a whole bunch of copied druid beast shapes I wanna just drop a token onto the field and run the API to assign.&nbsp; This way I can mass convert their mental stats and everything that needs changed to the druid character's, not effecting the original NPC sheets, with Tokenmod and CSA. Side Note:&nbsp; Why do Druids have to be so difficult/ frustrating when using Roll20?&nbsp; -_- (Rhetorical Question) Unfortunately those are two different fields when you edit a character sheet, In Player's Journals and Can Be Edited &amp; Controlled By, but tokens are only concerned with the latter as far as I can tell, because that's what determines who can select them.&nbsp; You'll notice that if you try to edit a token's properties, it has the Controlled By section which links to the Represents section if you enable that.&nbsp; There's no need to put a token (as opposed to a character sheet) in other players' journals, so that ability probably isn't linked to them in any way that TokenMod could manipulate. However, to bring your side note more to the forefront, I've got a TokenMod question for The Aaron myself, which just so happens to relate to Druids and WildShape, but could certainly be useful to others if possible. What are the chances that the --report functionality of TokenMod could be made to report the numerical value of currentside instead of returning "undefined"?&nbsp; I'm in the process of developing what I think will be a very good method for dealing with Wild Shapes, but I'm trying to make it as user-friendly as possible so others can benefit from it and being able to have commands like the ones below would be enormously helpful: !token-mod --set currentside#+1 --report player#"Current side is now {currentside:change}." !token-mod --report player#"Current side is {currentside}."
1582925228
The Aaron
Roll20 Production Team
API Scripter
For controlled by vs journal: token controlled by is determined by the Graphic (token) field, unless it represents a character, in which case it is determined by the character's controlled by field. If I only did the Graphic field, it would be a half solution and non-intuitive. It's a weird blend for TokenMod to also adjust the character that way, but was necessary. I can add the journal pretty easily, and I've long been considering adding character and other object modification options. Then I guess I'd just rename it "Mod". &nbsp;=D For the experimental report feature: I'll fix that. =D&nbsp;
1582926070
Dumbhuman
Pro
Marketplace Creator
And here I was trying to apologize for what I assumed was a built in limitation of TokenMod that prevented you from adding Journal stuff to its list of features!&nbsp; Now I'm sorry for doubting your abilities!
1582940456

Edited 1582942852
I never doubt TheAaron. As I said, this was still a more or less way of mass converting copies, since the labor was already annoying, I was thinking of ways I could limit my own annoyance.&nbsp; Was also wondering if I could eliminate "Copy of " somehow in the name field, which I think might better be served from CSA, now that I think of it. And no - it still should always be using the token as it's object of reference.&nbsp; So calling it "Mod" does not seem appropriate.&nbsp; It, like CSA, is just a huge powerscript that is capable of a great many things when fully realized.
I want to be able to toggle on and off the 'Show as Token Action' toggle for a selected ability on a character. Is this something that's possible with token-mod?
1584112314
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
That would require a specialized script. That's a journal setting, not a token setting. It's not even a character sheet setting AFAIK, so you likely couldn't even do it with ChatSetAttr.
1584117581
The Aaron
Roll20 Production Team
API Scripter
As Keith said. It wouldn't be a difficult script to write.&nbsp;
Can you recommend a good place to look for a complete newbie trying to figure out how to script for the roll20 API? I've got no idea what I'm doing, but I'd love to be able to have my parties Bard toggle on and off a bard inspiration Token Action for other players that they could click to roll the inspiration die (and would then untoggle the button itself)
1584156120
The Aaron
Roll20 Production Team
API Scripter
Ah, that makes the script a bit more complicated.&nbsp; One caveat is that in order for the Token Action button to appear or disappear when the setting is changed, you have to deselect the token. I whipped up a little script to do this.&nbsp; Here's how it works: For the bard, assuming an ability named Bard Inspiration , they just need to run something like this: !toggle-ability --enable --name bard inspiration --ids @{target|token_id} This will set istokenaction to true on all the matching abilities.&nbsp; The ability name just needs to have the same contiguous set of letters (case insensitive), so "bard insp" or "bardinspiration" or "inspiration" would all match the ability name. (if you're too in-specific, you'll end up matching a bunch, heads up).&nbsp; You can provide as many ids as you like, either token ids (as above) or character ids or a mix of both: !toggle-ability --enable --name granted --ids @{Grog the Great|character_id} @{Bob the Slayer|character_id} For the abilities, you can add: !toggle-ability and save the ability.&nbsp; The script will find that and update it to say something like: !toggle-ability --disable --ids -KQLK4d2vD1OvwyUldZT which will cause the ability have istokenaction set to false when it gets used. Here's the code: on('ready',()=&gt;{ const keyFormat = (text) =&gt; (text &amp;&amp; text.toLowerCase().replace(/\s+/g,'')) || undefined; const matchKey = (keys,subject) =&gt; subject &amp;&amp; !_.isUndefined(_.find(keys,(o)=&gt;(-1 !== subject.indexOf(o)))); const isString = (s)=&gt;'string'===typeof s || s instanceof String; const parse = (txt) =&gt; txt.split(/\s+--/).slice(1).reduce((m,a)=&gt;{ let a2 = a.split(/\s+/); let key = a2[0].toLowerCase(); switch(key){ case 'enable': case 'disable': m[key]=true; break; case 'name': m[key] = a.slice(key.length+1); m[`${key}_key`] = keyFormat(m[key]); break; case 'ids': m[key] = [...(m[key]||[]),...a2.slice(1)]; break; } return m; },{}); const s = { outer: `border:1px solid #999; border-radius: .5em; padding: .1em .3em; background-color: #ccc;`, err: `color: #990000;font-weight:bold;`, warn: `color: #ffc107;font-weight:bold;display:inline-block;background:#000;padding:.1em .3em;` }; const f = { err: (m)=&gt;`&lt;span style="${s.err}"&gt;${m}&lt;/span&gt;`, warn: (m)=&gt;`&lt;span style="${s.warn}"&gt;${m}&lt;/span&gt;`, code: (m)=&gt;`&lt;code&gt;${m}&lt;/code&gt;` }; const ids2chars = (ids)=&gt; (ids || []) .reduce((m,id) =&gt; [...m, {id, token: getObj('graphic',id)}] ,[]) .reduce((m,o)=&gt; [...m,(undefined === o.token ? o.id : o.token.get('represents'))],[]) .map(id =&gt; getObj('character',id)) .filter(g=&gt;undefined !== g) ; const findAbilities = (name, cs) =&gt; cs .map(c=&gt;c.id) .map(id =&gt; findObjs({type: 'ability', characterid: id}).filter(a=&gt;matchKey([name],keyFormat(a.get('name'))))[0]) .filter(o=&gt;undefined !==o); const say = (who,msg) =&gt; sendChat('',`/w "${who}" &lt;div style="${s.outer}"&gt;${msg}&lt;/div&gt;`); const leftDiff = (A,B) =&gt; B.filter(i =&gt; !A.includes(i)); on('change:ability:action',(obj)=&gt;{ let a = obj.get('action'); if(/^!toggle-ability\b\s*$/img.test(a)){ obj.set({action: a.replace(/^!toggle-ability\b\s*$/img,`!toggle-ability --disable --ids ${obj.id}`)}); } }); on('chat:message',(msg)=&gt;{ if('api'===msg.type &amp;&amp; /^!toggle-ability\b/i.test(msg.content)){ let cmd = parse(msg.content); let who = (getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); if(cmd.enable === cmd.disable){ say(who,`${f.err('Error:')} You must specify only one of either ${f.code('--enable')} or ${f.code('--disable')}.`); } else if(cmd.enable){ if(isString(cmd.name)){ if(Array.isArray(cmd.ids)){ let cs = ids2chars(cmd.ids); let missing = leftDiff(cmd.ids,cs.map(c=&gt;c.id)); if(missing.length){ say(who,`${f.warn('Notice:')} No characters found for ids: ${missing.join(', ')}`); } let abs = findAbilities(cmd.name_key,cs); let missing2 = leftDiff(abs.map(a=&gt;a.get('characterid')),cs.map(c=&gt;c.id)); if(missing2.length){ say(who,`${f.warn('Notice:')} Characters without the ${cmd.name} ability: ${cs.filter(c=&gt;missing2.includes(c.id)).map(c=&gt;c.get('name')).join(', ')}`); } abs.forEach(a=&gt;a.set({ istokenaction: true })); } else { say(who,`${f.err('Error:')} No IDs specified, use ${f.code('--ids')} to add token or character ids to find abilities.`); } } else { say(who,`${f.err('Error:')}: No Name specified, use ${f.code('--name')} to add a name (or name fragment) for the ability.`); } } else if(cmd.disable){ if(Array.isArray(cmd.ids)){ let abs = cmd.ids.map(id =&gt; getObj('ability',id)).filter(o=&gt;undefined !== o); let missing = leftDiff(cmd.ids, abs.map(a=&gt;a.id)); if(missing.length){ say(who,`${f.warn('Notice:')} No Abilities found for ids: ${missing.join(', ')}`); } abs.forEach(a=&gt;a.set({ istokenaction: false })); } else { say(who,`${f.err('Error:')} No IDs specified, use ${f.code('--ids')} to add the ability ids to hide.`); } } } }); }); There are probably a bunch of enhancements you could make to this (like checking all the abilities at start up to make sure the generated code is still valid, such as when you clone a character and end up with a new ability id), but this is a pretty functional start. As for learning the API, these are some good places to start: <a href="https://app.roll20.net/forum/post/66605115/namespaces-novice-seeks-help-exploring-the-revealing-module-pattern" rel="nofollow">https://app.roll20.net/forum/post/66605115/namespaces-novice-seeks-help-exploring-the-revealing-module-pattern</a> <a href="https://app.roll20.net/forum/post/6584105/creating-an-object-that-holds-specific-character-dot-id-and-character-name/?pagenum=1" rel="nofollow">https://app.roll20.net/forum/post/6584105/creating-an-object-that-holds-specific-character-dot-id-and-character-name/?pagenum=1</a> <a href="https://app.roll20.net/forum/post/6237754/slug%7D" rel="nofollow">https://app.roll20.net/forum/post/6237754/slug%7D</a>
1584178599

Edited 1584183205
Omg that's amazing. I didn't expect you to actually make something. Thank you so much! Quick followup, is there a way for token-mod and toggle-ability to target off the same target query? This is what I have so far !token-mod --ids @{target|Select creature to inspire|token_id} --set statusmarkers#+Bardic-Inspiration !toggle-ability --enable --name bardic inspiration --ids @{target|token_id} But it prompts too target queries and just having one would be ideal Also I keep getting Notice: &nbsp;No characters found for ids: -Lc2_PJWU8K1PoHtnahZ Update: I tried solve the multiselection issue using another one of your scripts Aaron, !gsa. I would store the targets ID as an attribute on the bard, then call that for targeting. The problem is that it seems to resolve the whole macro before updating the stored ID, meaning the previously selected character gets inspried, Any suggestions?
The Aaron said: Ah, that makes the script a bit more complicated.&nbsp; One caveat is that in order for the Token Action button to appear or disappear when the setting is changed, you have to deselect the token. I whipped up a little script to do this.&nbsp; Here's how it works: For the bard, assuming an ability named Bard Inspiration , they just need to run something like this: !toggle-ability --enable --name bard inspiration --ids @{target|token_id} This will set istokenaction to true on all the matching abilities.&nbsp; The ability name just needs to have the same contiguous set of letters (case insensitive), so "bard insp" or "bardinspiration" or "inspiration" would all match the ability name. (if you're too in-specific, you'll end up matching a bunch, heads up).&nbsp; You can provide as many ids as you like, either token ids (as above) or character ids or a mix of both: !toggle-ability --enable --name granted --ids @{Grog the Great|character_id} @{Bob the Slayer|character_id} For the abilities, you can add: !toggle-ability and save the ability.&nbsp; The script will find that and update it to say something like: !toggle-ability --disable --ids -KQLK4d2vD1OvwyUldZT which will cause the ability have istokenaction set to false when it gets used. Here's the code: on('ready',()=&gt;{ const keyFormat = (text) =&gt; (text &amp;&amp; text.toLowerCase().replace(/\s+/g,'')) || undefined; const matchKey = (keys,subject) =&gt; subject &amp;&amp; !_.isUndefined(_.find(keys,(o)=&gt;(-1 !== subject.indexOf(o)))); const isString = (s)=&gt;'string'===typeof s || s instanceof String; const parse = (txt) =&gt; txt.split(/\s+--/).slice(1).reduce((m,a)=&gt;{ let a2 = a.split(/\s+/); let key = a2[0].toLowerCase(); switch(key){ case 'enable': case 'disable': m[key]=true; break; case 'name': m[key] = a.slice(key.length+1); m[`${key}_key`] = keyFormat(m[key]); break; case 'ids': m[key] = [...(m[key]||[]),...a2.slice(1)]; break; } return m; },{}); const s = { outer: `border:1px solid #999; border-radius: .5em; padding: .1em .3em; background-color: #ccc;`, err: `color: #990000;font-weight:bold;`, warn: `color: #ffc107;font-weight:bold;display:inline-block;background:#000;padding:.1em .3em;` }; const f = { err: (m)=&gt;`&lt;span style="${s.err}"&gt;${m}&lt;/span&gt;`, warn: (m)=&gt;`&lt;span style="${s.warn}"&gt;${m}&lt;/span&gt;`, code: (m)=&gt;`&lt;code&gt;${m}&lt;/code&gt;` }; const ids2chars = (ids)=&gt; (ids || []) .reduce((m,id) =&gt; [...m, {id, token: getObj('graphic',id)}] ,[]) .reduce((m,o)=&gt; [...m,(undefined === o.token ? o.id : o.token.get('represents'))],[]) .map(id =&gt; getObj('character',id)) .filter(g=&gt;undefined !== g) ; const findAbilities = (name, cs) =&gt; cs .map(c=&gt;c.id) .map(id =&gt; findObjs({type: 'ability', characterid: id}).filter(a=&gt;matchKey([name],keyFormat(a.get('name'))))[0]) .filter(o=&gt;undefined !==o); const say = (who,msg) =&gt; sendChat('',`/w "${who}" &lt;div style="${s.outer}"&gt;${msg}&lt;/div&gt;`); const leftDiff = (A,B) =&gt; B.filter(i =&gt; !A.includes(i)); on('change:ability:action',(obj)=&gt;{ let a = obj.get('action'); if(/^!toggle-ability\b\s*$/img.test(a)){ obj.set({action: a.replace(/^!toggle-ability\b\s*$/img,`!toggle-ability --disable --ids ${obj.id}`)}); } }); on('chat:message',(msg)=&gt;{ if('api'===msg.type &amp;&amp; /^!toggle-ability\b/i.test(msg.content)){ let cmd = parse(msg.content); let who = (getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); if(cmd.enable === cmd.disable){ say(who,`${f.err('Error:')} You must specify only one of either ${f.code('--enable')} or ${f.code('--disable')}.`); } else if(cmd.enable){ if(isString(cmd.name)){ if(Array.isArray(cmd.ids)){ let cs = ids2chars(cmd.ids); let missing = leftDiff(cmd.ids,cs.map(c=&gt;c.id)); if(missing.length){ say(who,`${f.warn('Notice:')} No characters found for ids: ${missing.join(', ')}`); } let abs = findAbilities(cmd.name_key,cs); let missing2 = leftDiff(abs.map(a=&gt;a.get('characterid')),cs.map(c=&gt;c.id)); if(missing2.length){ say(who,`${f.warn('Notice:')} Characters without the ${cmd.name} ability: ${cs.filter(c=&gt;missing2.includes(c.id)).map(c=&gt;c.get('name')).join(', ')}`); } abs.forEach(a=&gt;a.set({ istokenaction: true })); } else { say(who,`${f.err('Error:')} No IDs specified, use ${f.code('--ids')} to add token or character ids to find abilities.`); } } else { say(who,`${f.err('Error:')}: No Name specified, use ${f.code('--name')} to add a name (or name fragment) for the ability.`); } } else if(cmd.disable){ if(Array.isArray(cmd.ids)){ let abs = cmd.ids.map(id =&gt; getObj('ability',id)).filter(o=&gt;undefined !== o); let missing = leftDiff(cmd.ids, abs.map(a=&gt;a.id)); if(missing.length){ say(who,`${f.warn('Notice:')} No Abilities found for ids: ${missing.join(', ')}`); } abs.forEach(a=&gt;a.set({ istokenaction: false })); } else { say(who,`${f.err('Error:')} No IDs specified, use ${f.code('--ids')} to add the ability ids to hide.`); } } } }); }); There are probably a bunch of enhancements you could make to this (like checking all the abilities at start up to make sure the generated code is still valid, such as when you clone a character and end up with a new ability id), but this is a pretty functional start. As for learning the API, these are some good places to start: <a href="https://app.roll20.net/forum/post/66605115/namespaces-novice-seeks-help-exploring-the-revealing-module-pattern" rel="nofollow">https://app.roll20.net/forum/post/66605115/namespaces-novice-seeks-help-exploring-the-revealing-module-pattern</a> <a href="https://app.roll20.net/forum/post/6584105/creating-an-object-that-holds-specific-character-dot-id-and-character-name/?pagenum=1" rel="nofollow">https://app.roll20.net/forum/post/6584105/creating-an-object-that-holds-specific-character-dot-id-and-character-name/?pagenum=1</a> <a href="https://app.roll20.net/forum/post/6237754/slug%7D" rel="nofollow">https://app.roll20.net/forum/post/6237754/slug%7D</a> Davyd said: Omg that's amazing. I didn't expect you to actually make something. Thank you so much! Quick followup, is there a way for token-mod and toggle-ability to target off the same target query? This is what I have so far !token-mod --ids @{target|Select creature to inspire|token_id} --set statusmarkers#+Bardic-Inspiration !toggle-ability --enable --name bardic inspiration --ids @{target|token_id} But it prompts too target queries and just having one would be ideal Also I keep getting Notice: &nbsp;No characters found for ids: -Lc2_PJWU8K1PoHtnahZ Update: I tried solve the multiselection issue using another one of your scripts Aaron, !gsa. I would store the targets ID as an attribute on the bard, then call that for targeting. The problem is that it seems to resolve the whole macro before updating the stored ID, meaning the previously selected character gets inspried, Any suggestions? This might be one to take outside of Token-Mod; creating it's own forum post - bc this is really good and deserves its own post! (Why did I even need to enter that redundancy?&nbsp; It's TheAaron's code!&nbsp; Of course it good!)
Agreed on all counts, sorry, got carried away with how awesome and helpful TheAaron is.
1584192444

Edited 1584192551
Davyd said: Agreed on all counts, sorry, got carried away with how awesome and helpful TheAaron is. +100 on that.... I think TheAaron breathes in air, and exhales a complex Einstein-Level code in script ...
1584207246
The Aaron
Roll20 Production Team
API Scripter
Yeah, I should make a new post for it I guess. In the meantime, try this: !token-mod --ids @{target|Select creature to inspire|token_id} --set statusmarkers#+Bardic-Inspiration !toggle-ability --enable --name bardic inspiration --ids @{target| Select creature to inspire| token_id}
Hi everyone :) I'm fairly new to this script, and was redirected here from another thread :) Is there a way to set a token variable (like the sight range, or an aura size) based on a sheet attribute, like ranks in a Skill for Pathfinder?
1584459012
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Yes. You would need to know the name of the attribute to pull the value from. If you give a specific example, I'm sure someone can lend you a hand.
1584631687

Edited 1584631749
Dan said: Hi everyone :) I'm fairly new to this script, and was redirected here from another thread :) Is there a way to set a token variable (like the sight range , or an aura size) based on a sheet attribute, like ranks in a Skill for Pathfinder? keithcurtis said: Yes. You would need to know the name of the attribute to pull the value from. If you give a specific example, I'm sure someone can lend you a hand. Hi Dan, Keith is correct: The following is what I use to set vision, setup in a macro.&nbsp; You must have a character selected to do this.&nbsp; This uses the Shaped character sheet: /w gm &amp;{template: 5e-shaped }{{ title =Adjusting vision for **@{selected|token_name}**}}{{ subheader =to token-mod's parameters of:}}{{ text_big =?{Vision|Torch, light_radius#40 light_dimradius#20 light_hassight#yes light_angle#360 light_otherplayers#yes|Light Cantrip, light_radius#40 light_dimradius#20 light_hassight#yes light_angle#360 light_otherplayers#yes|Hooded Lantern, light_radius#60 light_dimradius#30 light_hassight#yes light_angle#360 light_otherplayers#yes|Bullseye Lantern, light_radius#120 light_dimradius#60 light_angle#60 light_hassight#yes light_otherplayers#yes|Lamp, light_radius#30 light_dimradius#15 light_hassight#yes light_angle#360 light_otherplayers#yes|Candle, light_radius#5 light_dimradius#=0 light_hassight#yes light_angle#360 light_otherplayers#yes|Darkvsion (Spanner 30'), light_radius#30 light_dimradius#=-5 light_hassight#yes light_angle#360 light_otherplayers#no|Darkvision (Std 60'), light_radius#60 light_dimradius#=-5 light_hassight#yes light_angle#360 light_otherplayers#no|Darkvision (90'), light_radius#90 light_dimradius#=-5 light_hassight#yes light_angle#360 light_otherplayers#no|Darkvision (Drow 120'), light_radius#120 light_dimradius#=-5 light_hassight#yes light_angle#360 light_otherplayers#no|Warlock Devil's Sight, light_radius#120 light_dimradius#=120 light_hassight#yes light_angle#360 light_otherplayers#no|No light source(Dusk), light_radius#120 light_dimradius#=-5 light_hassight#yes light_angle#360 light_otherplayers#no|Fog, light_radius#200 light_dimradius#=5 light_hassight#yes light_angle#360 light_otherplayers#no|No light source, light_radius#10 light_dimradius#=-5 light_hassight#yes light_angle#360 light_otherplayers#no|Blinded, light_hassight#no light_angle#360 light_otherplayers#no statusmarkers#+bleeding-eye|Flametongue Weapon, light_radius#40 light_dimradius#40 light_hassight#yes light_angle#360 light_otherplayers#yes|Sun Blade, light_radius#30 light_dimradius#30 light_hassight#yes light_angle#360 light_otherplayers#yes} }} !token-mod --set ?{Vision} The following needs to be changed for the OGL sheet: /w gm &amp;{template: npcaction }{{ rname =Adjusting vision for **@{selected|token_name}**}}{{ name =to token-mod's parameters of:}}{{ description =?{Vis......
First off, thank you for this tokenmod thing, it's amazing.&nbsp; I've seen a lot of discussion about some very advanced stuff, but is there some documentation somewhere of what all the tokenmod commands are? I'm looking to do some pretty simple macros.&nbsp; Heck, if someone has a link to "here are the tokenmod macros I use in my campaign", I can learn by example... painfully.&nbsp; Just hoping for a list of things in one place - an "idiot's guide" so to speak. I have googled and searched this forum, but all I found was a wiki page that hasn't been updated in 5 years or so, and google thinks all discussions on this forum happened in 2020, so that's no help.
1585230812
The Aaron
Roll20 Production Team
API Scripter
In game, you can issue: !token-mod --help for a full text help that tells you the "what" of TokenMod. Probably the forum is the best place to find examples.&nbsp; I bet if you started a thread asking for TokenMod examples, you'd get them in droves.
1585600335

Edited 1585600363
Hello, I'm trying to setup a chat menu to generate random names using token mod. My macro looks like this : /w gm [Orcs](!token-mod --set name|"[[ 1t[Orcs_Names] ]]") | [Hobgoblins](!token-mod --set name|"[[ 1t[Hobgoblins_Names] ]]") | [Goblins](!token-mod --set name|"[[ 1t[Goblins_Names] ]]") | [Kobolds](!token-mod --set name|"[[ 1t[Kobolds_Names] ]]") | [Bugbears](!token-mod --set name|"[[ 1t[Bugbears_Names] ]]")&nbsp; When I click a button the only thing the APi is receiving is !token-mod --set name| but when, for example, I enter !token-mod --set name|"[[ 1t[Hobgoblins_Names] ]]" directly in the chat it works as intended. Any Ideas ?
1585605159

Edited 1585605899
The Aaron
Roll20 Production Team
API Scripter
To get that to work, you'd need to HTML encode the [[ parts (or at least one of them) so they only get expanded when you click the button.&nbsp; Try this: /w gm [Orcs](!token-mod --set name|"&amp;lbrack;[ 1t[Orcs_Names] ]]") | [Hobgoblins](!token-mod --set name|"&amp;lbrack;[ 1t[Hobgoblins_Names] ]]") | [Goblins](!token-mod --set name|"&amp;lbrack;[ 1t[Goblins_Names] ]]") | [Kobolds](!token-mod --set name|"&amp;lbrack;[ 1t[Kobolds_Names] ]]") | [Bugbears](!token-mod --set name|"&amp;lbrack;[ 1t[Bugbears_Names] ]]") Edit: Hmm.. that's not precisely working either.&nbsp; I might suggest the use of a Macro Character &nbsp;to avoid needing to escape the commands.
Hey everyone, I am new to API scripts and have been searching through a lot of posts to try to find an answer to this question: Can I set a macro to set the HP of a Character in Bar 1 and have it be greater than a certain number? I just rolled a scenario and I had 3 separate Kobold enemies come up as 1 or 0 HP. I was okay with the 1, but the 0 made me laugh at the thought of a dead Kobold hiding in the corner.
1585634951
The Aaron
Roll20 Production Team
API Scripter
You can use dice groups for that, something like this (imagine the 1d6-3 is from an attribute) will give a minimum of 3: !token-mod --set bar1_current|[[{[[1d6-2]],3}kh1]]
1585641019

Edited 1585641680
The Aaron said: To get that to work, you'd need to HTML encode the [[ parts (or at least one of them) so they only get expanded when you click the button.&nbsp; Try this: /w gm [Orcs](!token-mod --set name|"&amp;lbrack;[ 1t[Orcs_Names] ]]") | [Hobgoblins](!token-mod --set name|"&amp;lbrack;[ 1t[Hobgoblins_Names] ]]") | [Goblins](!token-mod --set name|"&amp;lbrack;[ 1t[Goblins_Names] ]]") | [Kobolds](!token-mod --set name|"&amp;lbrack;[ 1t[Kobolds_Names] ]]") | [Bugbears](!token-mod --set name|"&amp;lbrack;[ 1t[Bugbears_Names] ]]") Edit: Hmm.. that's not precisely working either.&nbsp; I might suggest the use of a Macro Character &nbsp;to avoid needing to escape the commands. &nbsp; My macro is already in a Macro Character (in the Attributes/abilitities tab). Your code is giving me :&nbsp; SyntaxError: Expected "(", ".", "[", "abs(", "ceil(", "d", "floor(", "round(", "t", "{", [ |\t], [+|\-] or [0-9] but "g" found. Thx Aaron for your answer (and all your work !)
1585671630

Edited 1585671674
Dumbhuman
Pro
Marketplace Creator
sebastien g. said: The Aaron said: To get that to work, you'd need to HTML encode the [[ parts (or at least one of them) so they only get expanded when you click the button.&nbsp; Try this: /w gm [Orcs](!token-mod --set name|"&amp;lbrack;[ 1t[Orcs_Names] ]]") | [Hobgoblins](!token-mod --set name|"&amp;lbrack;[ 1t[Hobgoblins_Names] ]]") | [Goblins](!token-mod --set name|"&amp;lbrack;[ 1t[Goblins_Names] ]]") | [Kobolds](!token-mod --set name|"&amp;lbrack;[ 1t[Kobolds_Names] ]]") | [Bugbears](!token-mod --set name|"&amp;lbrack;[ 1t[Bugbears_Names] ]]") Edit: Hmm.. that's not precisely working either.&nbsp; I might suggest the use of a Macro Character &nbsp;to avoid needing to escape the commands. &nbsp; My macro is already in a Macro Character (in the Attributes/abilitities tab). Your code is giving me :&nbsp; SyntaxError: Expected "(", ".", "[", "abs(", "ceil(", "d", "floor(", "round(", "t", "{", [ |\t], [+|\-] or [0-9] but "g" found. Thx Aaron for your answer (and all your work !) I'm pretty sure Aaron means to have all the subcommands to be used in the button menu as separate abilities on the macro character so that they can be called with this format: /w gm [Orcs](~MacroCharacter|OrcNames) [Hobgoblins](~MacroCharacter|HobgoblinNames) [Goblins](~MacroCharacter|GoblinNames) ... For whatever reason, that format works a lot more consistently than putting straight API or macro calls inside the parentheses.
1585671802
The Aaron
Roll20 Production Team
API Scripter
Yes, that exactly.&nbsp; Sorry... busy times, meant to get back to this.&nbsp; Thanks KC ., you're spot on!
1585680784

Edited 1585703535
Thx to both of you ! busy times here too, will try that Edit : It works like a charm, Thx again