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.

February 04 (8 years ago)
Awesome!
February 08 (8 years ago)
The Aaron
Pro
API Scripter
Update v0.8.24 -- Added the ability to set the defaulttoken on a character. You must set the represents of the token to a character before using defaulttoken, but it works just as you'd expected:
!token-mod --set defaulttoken
You can of course chain this with other set commands:
!token-mod {{
  --set
    represents|@{bob|character_id}
    bar1_link|hp
    bar2_link|speed
    bar3_link|ac
    controlledby|tom|sally|nancy
    statusmarkers|blue:3|red:[[1d6]]
    defaulttoken
}}
Be sure to set the default token after all the settings you want preserved. For example, if you set bar3_link after, it won't be set when you drag the token out later. (Thanks Jakob!)
February 08 (8 years ago)

Edited February 08 (8 years ago)
Jakob
Sheet Author
API Scripter
Huh, finally :).
February 08 (8 years ago)
The Aaron
Pro
API Scripter
Tell me about it!  =D  Nice not to be in a constant state of moving or the like. =D  Maybe I can finally get some things done.
February 10 (7 years ago)

Edited February 10 (7 years ago)
Awesome update, Aaron! This has made my setup process so much easier.

One weird little quirky is bothering me though - when you link to the 5E OGL sheet's speed using something like: --set bar1_link|npcd_speed, it brings in the entire string of "30 feet." instead of just "30". Manually linking strips off the "feet." part. Is that possible to do via TokenMod?

And while I'm begging, is there any way to add support for TokenNameNumber? :) Not sure if its possible to edit the name of the token via the API, but it would be great to automagically turn something like "Ogre" into "Ogre %%NUMBERED%%"

Edit: derp, just saw this. never mind the second part :)
February 11 (7 years ago)
The Aaron
Pro
API Scripter
For the 30, you could put it in an inline roll. 
February 11 (7 years ago)
The Aaron
Pro
API Scripter
Update v0.8.25 -- Fixed bug with defaulttoken where it didn't set correctly on the character. (Thanks Mike B.)
February 11 (7 years ago)
The Aaron
Pro
API Scripter

The Aaron said:

For the 30, you could put it in an inline roll. 
Just reread what you said.  With linking, you're at the mercy of what the attribute has in it.  You could set the value with an inline roll:
!token-mod --set bar3_value|[[@{selected|npcd_speed]]

Gotcha - that makes sense. Thanks Aaron!
March 08 (7 years ago)

Edited March 08 (7 years ago)
Aaron, Sorry if I missed it before, but is there any way for the players to use this to mark NPCs they don't control?
Im looking for a good way to make a Hunter's Mark macro for 5e, something simple like adding the red dot status icon.

I tried your old Mark script, but problem with that is it lays the image on top of the NPC token, and doesn't follow the NPC when moved.
March 09 (7 years ago)

Edited March 09 (7 years ago)
The Aaron
Pro
API Scripter
Definitely!  You need to turn on the "Players can IDs" setting in the config:
!token-mod --help

Then they can use:
!token-mod --set statusmarkers|!archery-target --ids @{target|token_id}
To toggle it on and off. 
I gotta quit taking breaks from roll20... I totally forgot about the --ids @{target|token_id} ability. Thanks man!
March 09 (7 years ago)
The Aaron
Pro
API Scripter
No problem!  Happy Rolling!
March 09 (7 years ago)

Edited March 09 (7 years ago)
Hey trying to figure this out. So I have specific characters that I want to have affected by a macro. Like a single press.
Specifically I'm trying to figure out if there's a way to modify tokens via character name as opposed to token id.
Sort of a @{"Character Name 1"|token_id} type of thing

This is the macro I have right now. It affects the Lowlight and Darkvision of particular characters. I have one that also turns it off. This toggle is needed since I'm doing a face-to-face game and Roll20 has weird problems with Sight multipliers and multiple tokens controlled by the same player.
!token-mod {{
--ignore-selected 
--ids
-KekZFFMOlXE_D4E68VF
--set
light_radius|60
}}
!token-mod {{
--ignore-selected
--ids
-KekZFFMOlXE_D4E68VF
-KekmGhKlBiwbqJoJoZ5
--set
light_multiplier|2
}}
This is the macro I want... but maybe this is already possible. But when I try it I just get an error saying "No character was found for 'Character Name 1'". All player tokens are controlled by the same Player account (since it is face to face) and the "Represents" setting is correctly set.
!token-mod {{
--ignore-selected 
--ids
@{"Character Name 1"|token_id}
--set
light_radius|60
}}
!token-mod {{
--ignore-selected
--ids
@{"Character Name 1"|token_id}
@{"Character Name 2"|token_id}
--set
light_multiplier|2
}}
Am I just doing this wrong? Or does such functionality need to be added? I know you already have a way to pull token_ids via character names... Just can't figure out how to do this here. Since I'd rather not manually change the token_ids all the time, if I have to use targeting then I will... but I'd rather be able to do it by name.
March 10 (7 years ago)

Edited March 10 (7 years ago)
The Aaron
Pro
API Scripter
Since many tokens can all refer to the same character (one per map often for player characters, many per page for monsters), there is no attribute of token_id on characters.  You'd want to leave the "" off anyway when referring to the character's attributes in general.

So, there isn't a way to do this right now....

However, if you change the if check on line 1621 to this:
                if(ids.length){
                    _.chain(ids)
                    .uniq()
                    .map(function(t){
                        return {
                            id: t,
                            token: getObj('graphic',t),
                            character: getObj('character',t)
                        };
                    })
                    .reduce(function(m,o){
                        if(o.token){
                            m.push(o.token.id);
                        } else if(o.character){
                            m=_.union(m,findObjs({type:'graphic',represents:o.character.id}));
                        }
                        return m;
                    },[])
                    .reject(_.isUndefined)
                    .each(function(t) {
                        applyModListToToken(modlist,t);
                    });
                }
Then you should be able to do:
!token-mod {{
--ignore-selected 
--ids
 @{Character Name 1|character_id}
--set
 light_radius|60
}}
!token-mod {{
--ignore-selected 
--ids
 @{Character Name 1|character_id}
 @{Character Name 2|character_id}
--set
 light_multiplier|2
}}
and it will find all tokens representing the specified character_id and adjust them on every map in one go.

March 10 (7 years ago)
The Aaron
Pro
API Scripter
Note: I've not tested this code yet...
I'll give it a shot, and modifying it on multiple maps is no problem for me. Thanks!
March 10 (7 years ago)

Edited March 10 (7 years ago)
So far so good! Works perfectly as far as I can tell.

EDIT: Well now it doesn't work when I don't have --ignore-selected in a macro. So it basically doesn't work for the regular functions. I get an error in the API (EDIT2: Redid the error with other scripts disabled. Macro used to reproduce error was !token-mod --flip light_hassight while selecting a token)
ReferenceError: t is not defined
ReferenceError: t is not defined
    at apiscript.js:1633:36
    at iterator (/home/node/d20-api-server/node_modules/underscore/underscore.js:184:16)
    at Function.<anonymous> (/home/node/d20-api-server/node_modules/underscore/underscore.js:199:14)
    at _.(anonymous function) [as reduce] (/home/node/d20-api-server/node_modules/underscore/underscore.js:1496:34)
    at handleInput (apiscript.js:1631:22)
    at eval (eval at <anonymous> (/home/node/d20-api-server/api.js:146:34), <anonymous>:65:16)
    at Object.publish (eval at <anonymous> (/home/node/d20-api-server/api.js:146:34), <anonymous>:70:8)
    at /home/node/d20-api-server/api.js:1510: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)

March 10 (7 years ago)
The Aaron
Pro
API Scripter
Whoops!  Fixed.  Changed:
m.push(t);
to
m.push(o.token.id);
March 10 (7 years ago)

Edited March 10 (7 years ago)

The Aaron said:

Whoops!  Fixed.  Changed:
m.push(t);
to
m.push(o.token.id);

fixed that for ya
m.push(o.token);
Got a really odd error when I used !token-mod --flip light_hassight
TypeError: token.get is not a function
TypeError: token.get is not a function
    at applyModListToToken (apiscript.js:1277:36)
    at apiscript.js:1641:8
    at Function._.each._.forEach (/home/node/d20-api-server/node_modules/underscore/underscore.js:153:9)
    at _.(anonymous function) [as each] (/home/node/d20-api-server/node_modules/underscore/underscore.js:1496:34)
    at handleInput (apiscript.js:1640:8)
    at eval (eval at <anonymous> (/home/node/d20-api-server/api.js:146:34), <anonymous>:65:16)
    at Object.publish (eval at <anonymous> (/home/node/d20-api-server/api.js:146:34), <anonymous>:70:8)
    at /home/node/d20-api-server/api.js:1510: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)
But changing it to what I mentioned above fixes the problem

If only Roll20 fixed the problem with sight multipliers being shared by ALL tokens that a player has control of when only 1 token has it.
March 10 (7 years ago)
The Aaron
Pro
API Scripter
Ah, there you go!  Thanks, I'll update my code... I shouldn't try to write code while fighting Strahd!  =D
March 11 (7 years ago)
I am trying to get this to work, but it seems it does not:

!token-mod {{
--set bar3_link|spell_save_dc
}}

Could you add "spell_save_dc" or should it work already? Working fine with hp/ac etc.
March 11 (7 years ago)
The Aaron
Pro
API Scripter
Is the token already representing a character?  check if @{selected|spell_save_dc} gives you a reference error in the chat.

Also, it's entirely possible the API won't be able to link an attribute that refers to a defaulted attribute which doesn't actually have an Attribute Object created.  If that's the case, I might be able to get it to work by setting the name instead of the id.  Let me know if it still doesn't work with the token representing the character and I'll see what I can do.
March 11 (7 years ago)
The Aaron
Pro
API Scripter
Update v0.8.26 -- Added the ability to specify character IDs to --ids and affect all tokens representing that character. (Thanks Hacchinya )
-- Added logic to link a bar to a sheet attribute if an actually Attribute Object doesn't exist. This has one quirk in that it won't set the values of the bars until something happens to cause Roll20 to review the token. Setting the token as a Default Token and dragging it back out causes this to happen, as does updating something that the set attr uses in a calculation (dexterity when it's a part of the initiative_bonus formula for example). You can also set the initial value with --bar1_value|@{selected|attribute} if you're only doing a single character. (Thanks Arno )
March 11 (7 years ago)
Thanks for the superfast response Aaron! The token is repping a character, I get this as output from @{selected|spell_save_dc}:

((floor((10-10)/2))+(8+(ceil((1)/1e10) + ceil((1)/4))+0))

That's also the content it now tries to put into the bar3. Total macro for completeness:

!token-mod {{
--on showname
light_hassight
showplayers_name
showplayers_bar1
showplayers_bar2
showplayers_bar3
showplayers_aura1
showplayers_aura2
--set represents|"?{Character Name}"
bar2_link|ac
bar1_link|hp
bar3_value|@{selected|spell_save_dc}
light_radius|5
light_dimradius|=-5
}}

March 11 (7 years ago)
The Aaron
Pro
API Scripter
The latest version should work for you, it will set it as a sheetattr_, which will allow it to be auto calculated.  If it doesn't work, I can take another look.  You'll have to manually install v0.8.26 until it gets rolled into the repo on Tuesday or the following tuesday.
March 11 (7 years ago)
Yep got it :)
"-=> TokenMod v0.8.26 <=- [Sat Mar 11 2017 16:10:51 GMT+0000 (UTC)]"

The output from the macro is as expected right? Otherwise ye, not working yet...thanks for your effort so far!
March 11 (7 years ago)
The Aaron
Pro
API Scripter
hmm.. wanna pm me an invite to your game and GM me?
March 12 (7 years ago)

Edited March 12 (7 years ago)
For anyone keeping track, the work-around right now is to use:

!token-mod {{
--on showname
light_hassight
showplayers_name
showplayers_bar1
showplayers_bar2
showplayers_bar3
showplayers_aura1
showplayers_aura2
--set represents|?{Character Name}
bar2_link|ac
bar1_link|hp
bar3_link|spell_save_dc
light_radius|5
light_dimradius|=-5
defaulttoken
}}

After running this, the token needs be re-dragged out of the journal for the tokens to update.
March 13 (7 years ago)

Edited March 13 (7 years ago)
Hey there all!

I've got a question, is there a way to limit the bar1_value to not go below 0?

Use case if that doesn't make sense:

  • Select all tokens on the page (inevitably, they will sometimes represent different characters with different hp's (aka different bar1_max))
  • Do damage to all (!token-mod --set bar1_value|-?{How much damage|0})

In this case, tokens with less max hp than others will have negative balance bars (sometimes being comically large and filling half the screen xD )

So, yeah. Anyway to make it so that the bar1_value never goes below 0?
March 13 (7 years ago)

Edited March 13 (7 years ago)
Really small api script to keep Bar1 at or above zero, but no higher than the max.

on("change:token", function (obj) {
	if (obj.get("subtype") != "token" || obj.get("isdrawing") == "true") return;
	if (parseInt(obj.get("bar1_value")) < 0 && parseInt(obj.get("bar1_max")) > 0) obj.set("bar1_value", 0);
	if (parseInt(obj.get("bar1_value")) > parseInt(obj.get("bar1_max"))) obj.set("bar1_value", parseInt(obj.get("bar1_max")));
});

SkyCaptainXIII said:

Really small api script to keep Bar1 at or above zero, but no higher than the max.

Thanks for making this!
Do I make a new script and put that code in there or do I put this in TokenMod somewhere with a manual install?

If the first answer (completely new script), then it isn't working.  : /
March 13 (7 years ago)
Oh... ah, that won't trigger off other script changes such as with token mod. There may be an option in token mod to achieve the same thing, not sure. I don't use it myself.
March 14 (7 years ago)

Edited March 14 (7 years ago)
The Aaron
Pro
API Scripter
You could actually modify HB's code to get a notification from TokenMod and enforce that constraint:
on('ready',function(){
	"use strict";
	
	var constrainBar1ToMax = function (obj) {
		if (obj.get("subtype") != "token" || obj.get("isdrawing") == "true") return;
		if (parseInt(obj.get("bar1_value")) < 0 && parseInt(obj.get("bar1_max")) > 0) obj.set("bar1_value", 0);
		if (parseInt(obj.get("bar1_value")) > parseInt(obj.get("bar1_max"))) obj.set("bar1_value", parseInt(obj.get("bar1_max")));
	};

	on("change:token", constrainBar1ToMax);

	if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){
		TokenMod.ObserveTokenChange(constrainBar1ToMax);
	}
});

The Aaron said:

You could actually modify HB's code to get a notification from TokenMod and enforce that constraint:

That did it Aaron! 
Thanks a lot man!
March 14 (7 years ago)
The Aaron
Pro
API Scripter
cool. =D
March 17 (7 years ago)
For the macro above, I am now trying to get Passive perception instead of spell save DC...not sure how that would work though, and not sure how to find out.

bar3_link|spell_save_dc --> bar3_link|passive_perception ?

Right now I can't even seem to find the list where I got "spell_save_dc" from in the first place.
March 17 (7 years ago)
The Aaron
Pro
API Scripter
It depends on what character sheet you're using.  If you find the number on a character sheet and right click, inspect it (in chrome), you can look for a name like 'attr_passive_perception' or the like.  It's that, without the 'attr_'.
March 18 (7 years ago)

The Aaron said:

It depends on what character sheet you're using.  If you find the number on a character sheet and right click, inspect it (in chrome), you can look for a name like 'attr_passive_perception' or the like.  It's that, without the 'attr_'.

Ah great, thanks for teaching me how to fish, that works :)

(n.b. it's passive_wisdom)
March 18 (7 years ago)
The Aaron
Pro
API Scripter
No worries. =D 
March 25 (7 years ago)

Edited March 25 (7 years ago)
I am trying to use TokenMod for a single damage macro, using the 5e Shaped sheet.

@{target|bar1_max} does not work, but that's what Chrome's inspect tells me on the max HP box in the token config panel.

Any ideas how to get the character's max health?
March 25 (7 years ago)
The Aaron
Pro
API Scripter
This is a quirk of the way character sheet attributes work. Anywhere you see an attribute ending in "_max" on a character sheet, you translate that to "|max" for an attribute reference in chat. Further, with @{target}, you must specify a label to make it work, so your final reference will be:
@{target|choose a target|bar1|max}
You can replace "choose a target" with whatever you like, that's the label, what gets shown on the query. Any where you use the same label it will only prompt you once, so you could do:
@{target|choose a target|character_name} has @{target|choose a target|bar1}/@{target|choose a target|bar1|max} hp. 
and it would only ask you which token once. 
March 25 (7 years ago)

The Aaron said:

This is a quirk of the way character sheet attributes work. Anywhere you see an attribute ending in "_max" on a character sheet, you translate that to "|max" for an attribute reference in chat. Further, with @{target}, you must specify a label to make it work, so your final reference will be:
@{target|choose a target|bar1|max}
You can replace "choose a target" with whatever you like, that's the label, what gets shown on the query. Any where you use the same label it will only prompt you once, so you could do:
@{target|choose a target|character_name} has @{target|choose a target|bar1}/@{target|choose a target|bar1|max} hp. 
and it would only ask you which token once. 

Ah that's it, thanks! If it's useful for anyone else, here's two easy macro's for single-target damage/healing, compatible with HealColors and safe from negative health and over-max-HP-health:

Healing
!token-mod {{
--set
bar1_value|[[ { [[ @{target|target|bar1} + ?{Heal} ]], @{target|target|bar1|max}}dh1 ]]
--ids
@{target|token_id}
}}

Damage
!token-mod {{
--set
bar1_value|[[ { [[ @{target|HP} - ?{Damage} ]], 0}kh1 ]]
--ids
@{target|token_id}
}}
April 03 (7 years ago)
The Aaron
Pro
API Scripter
Update v0.8.28 -- Added support for multiply (*) and divide (/) relative change options. Now you can do something like this:
!token-mod --set height|*2 width|*2
To double the size of your token, or this:
!token-mod --set height|/2 width|/2
To shrink it back down again.

Update v0.8.27 -- Fixed method for getting the display name of the current player so it won't crash when other api scripts execute API commands with sendChat() (Thanks Sky).
April 03 (7 years ago)
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
My duergar applaud this feature.
April 03 (7 years ago)
Currently with v0.8.27:
!token-mod --help
gives me:
For reference, the error message generated was: ReferenceError: msg is not defined
ReferenceError: msg is not defined
    at showHelp (apiscript.js:5721:28)
    at handleInput (apiscript.js:7016:8)
    at eval (eval at <anonymous> (/home/node/d20-api-server/api.js:146:1), <anonymous>:65:16)
    at Object.publish (eval at <anonymous> (/home/node/d20-api-server/api.js:146:1), <anonymous>:70:8)
    at /home/node/d20-api-server/api.js:1510: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)

April 03 (7 years ago)
The Aaron
Pro
API Scripter
Thanks Arno!  That should be fixed now, v0.8.29
April 03 (7 years ago)
The Aaron
Pro
API Scripter
Update v0.8.29 -- Fixed bug in who determination in showHelp(). (Thanks Arno)
April 07 (7 years ago)
The Aaron
Pro
API Scripter
Update v0.8.30 -- Fixed bug in who determination in showHelp(). (Thanks BGB)
v0.8.31; 5e OGL Sheet (Storm King's Thunder adventure loaded)
The Command (per the instructions)
!token-mod --set aura1_radius|

Produces the following error and crashes the Sandbox

TypeError: Cannot read property 'match' of undefined
TypeError: Cannot read property 'match' of undefined
    at parseArguments (apiscript.js:1020:35)
    at Function._.map._.collect (/home/node/d20-api-server/node_modules/underscore/underscore.js:172:24)
    at _.(anonymous function) [as map] (/home/node/d20-api-server/node_modules/underscore/underscore.js:1496:34)
    at parseSetArguments (apiscript.js:1212:5)
    at handleInput (apiscript.js:1599:20)
    at eval (eval at <anonymous> (/home/node/d20-api-server/api.js:146:1), <anonymous>:65:16)
    at Object.publish (eval at <anonymous> (/home/node/d20-api-server/api.js:146:1), <anonymous>:70:8)
    at /home/node/d20-api-server/api.js:1510: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)

I can't tell from the instructions if it's been updated with differing commands or not from when it worked before so if I'm using it incorrectly let me know.

After some testing (I'm NOT a programmer/coder/etc.) I finally found that the following works as it originally was:

!token-mod --set aura1_radius|0

Secondary Slightly Related Effect - From what little I understand, I had thought that the token aura values would need to be blank for the aura to be completely removed.  The API does remove the auras but the method I discovered above opens up a new (almost invisible but manually fixable) problem if I'm not careful.  Upon further testing, if I use !token-mod --set aura1_radius|0, then open the token properties and then save it with "0" values in the aura radius (as was applied by the API) it will create the slightest colored outline of the token.  I have to manually remove the 0 values first or it creates the colored outline. (I can provide images if I'm not clear in my description).

Like I said, I don't understand the code-level design but I suspect the API bugged out because the aura 1_radius command is looking for a number but gets an 'undefined' error when using the original syntax I found in the instructions.