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] Spawn Default Token Thread 2

1674347965

Edited 1674348275
Jim
Pro
Hi David, I've been using this API to great effect, thanks for providing it!   There is one last issue that I'm still struggling with, and it isn't a question about this script itself, but more about how to use it with token properties.  My goal is to select a token and use this script to spawn a new token, and have the new token inherit the same token name and tint color as the original token.  I am using the following command which correctly assigns the original token name to the new token, and it assigns a hard-coded green tint to the new token, so I know that !spawn is able to assign a tint correctly.  !Spawn --name|@{selected|character_name} --offset|1,0 --isDrawing|1 --tokenName|@{selected|token_name}  --tokenProps|tint_color:#00ff00 What I want to do next is have the new token inherit the same tint color as the original token, but I can't figure out how to get the original token's tint color. I've tried a number of variations on the following command, but I can't find an attribute name to use with @{selected|...} for the tint color.   !Spawn --name|@{selected|character_name} --offset|1,0 --isDrawing|1 --tokenName|@{selected|token_name}  --tokenProps|@{selected|tint_color} I've looked on the wiki but I can't find a list of supported attributes for the @{selected} function.  I've tried using object properties like tint_color, bar1_value, etc. and they don't work so the wiki's list of object properties is not the same as what you can get with @{selected}.  I've also tried looking at the TokenMod script, but it seems to mostly just set values, not get them.  At least I didn't see a way to query the tint_color using TokenMod.   Do you know of any way to get the original token's tint color so that !spawn can assign it to the new token too? Thanks again for your work on this mod, and answering questions! Jim Edit:  Or is there a way to have all of the original token's properties copied over to the !spawn's new token's tokenProps key/value list?  
Okay, I found a solution using the Fetch mod, which has the @(selected.tint_color) option among other things: !Spawn --name|@{selected|character_name} --offset|1,0 --tokenProps|tint_color:@(selected.tint_color), name:@(selected.token_name), showname:@(selected.showname), isdrawing:1 In the process of testing it, however, I found a new minor issue.  If I select multiple tokens and run that script, it will run it for all the selected tokens, but each one spawns using the last token's character name.  So if I run it on 3 tokens with different character names, it creates 3 new tokens but they are all copies of the third original token.  I tried using the SelectManager mod and !forselected but that didn't seem to do anything different.  I may not have had the syntax correct though.   For now, at least I have it working as long as only one token is selected at a time.   Best wishes, Jim
Actually, I'm still having trouble with the showname option in Spawn.  My command is: !Spawn --name|@{selected|character_name} --offset|1,0 --tokenProps|tint_color:@(selected.tint_color), name:@(selected.token_name), showname:@(selected.showname),  isdrawing:1 That always sets the new token's showname to true, regardless of what the original token's setting was.   When I test Fetch's @(selected.showname) it does return true or false correctly.   Even if I hardcode the spawn command to be showname:0  or showname:false,  it still creates the new token with showname turned on.  If I remove the showname entry from --tokenProps entirely, then the token shows up with showname off since that is the default token for the character sheet.   Am I using the showname property incorrectly in the spawn command?  Thanks! Jim
1674405793
David M.
Pro
API Scripter
Hmm, I am seeing the same behavior. Looking into it more, it doesn't look like a few of the boolean tokenProps are not working correctly. For example, showplayers_aura1 also ignores the 0/1 setting and always sets to true if included, but isdrawing 0/1 is respected. I'll have to look into it this. I've either done something stupid (likely) or there's a Roll20 bug (slightly less likely haha). Regardless, thanks for the heads-up.
What's your !forselected command line? Jim said: Okay, I found a solution using the Fetch mod, which has the @(selected.tint_color) option among other things: !Spawn --name|@{selected|character_name} --offset|1,0 --tokenProps|tint_color:@(selected.tint_color), name:@(selected.token_name), showname:@(selected.showname), isdrawing:1 In the process of testing it, however, I found a new minor issue.  If I select multiple tokens and run that script, it will run it for all the selected tokens, but each one spawns using the last token's character name.  So if I run it on 3 tokens with different character names, it creates 3 new tokens but they are all copies of the third original token.  I tried using the SelectManager mod and !forselected but that didn't seem to do anything different.  I may not have had the syntax correct though.   For now, at least I have it working as long as only one token is selected at a time.   Best wishes, Jim
1674484953
timmaugh
Pro
API Scripter
I'll wait for David's diagnosis of the showname property, but I seem to remember it being a bug that triggering it from a script didn't take effect until you manually "touched" the token and saved the settings again (even if you didn't make a manual change). To the point of processing multiple tokens at once, Colin has the right direction to use the forselected handle of SelectManager. Fetch is limited in the same way that Roll20 parsers are... in that if you select multiple tokens and issue the command: @{selected|token_name} You will get information from only 1 token. If you issue the command: !@(selected|token_name){&simple} You will get information from only 1 token. (The above requires Fetch and ZeroFrame) SelectManager lets you iterate over your tokens, one at a time, so you can get/deliver token specific information. You'd want to defer the Fetch resolution until each individual call, and you do that by including a parenthetically-enclosed character after the forselected handle: !forselected (^) Spawn --name|... ...and then using that same character to break up your Fetch constructions elsewhere in your command line: @^(selected.tint_color) SelectManager (who owns the forselected handle) will now issue your Spawn command line 1-time for each token you have selected, first removing the character you declare (here the caret). When the new command-line message comes through, Fetch is now able to recognize the construction and resolve it. Since SelectManager is only allowing 1 token to be "selected" for each of these outbound messages, you get the correct information returned.
Thank you all for the help.  I'm almost done (except for the possible bug with showname).  There's one last issue with !forselected that I'm trying to resolve. So, I have a few macros using !spawn that manipulate dice tokens (for the Cortex game system), to double (copy), step up (d6 to d8), step down (d8 to d6), etc.  With your help, they are now working when I have multiple tokens selected thanks to !forselected.  That is very helpful! There is one last macro that I'm struggling with.  The intent is that it should first make a copy of the selected dice token(s), then move one of each (either the original or the new copy) up to a region near the top of the map, using !token-mod for the move.  The following command works for a single token without using !forselected.  It copies the original token, then moves the original token up and leaves the copy in the original square: !Spawn --name|@(selected.character_name) --offset|0,0 --tokenProps|tint_color:@(selected.tint_color), name:@(selected.token_name), showname:@(selected.showname), isdrawing:1 !token-mod --set top|[[1d100/30+1]]g  When I try to add !forselected so that it can work with multiple dice tokens at once, it does correctly create the duplicates with !spawn, but I can't get the !token-mod to move one of each to the top of the map while leaving the other where it was originally.  If I use the following command, with !forselected in front of the !token-mod, it doesn't move either token, leaving the original and the new copy in the original square.   !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,0 --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 !forselected !token-mod --set top|[[1d100/30+1]]g  If I use the following command, without !forselected in front of the !token-mod, it now moves both tokens to the top of the map, instead of leaving the copy behind.  It seems like using !forselected on the first line for the !spawn causes the spawned copy to also be part of the selected tokens before !token-mod is fired off. !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,0 --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 !token-mod --set top|[[1d100/30+1]]g  Any ideas how I can get this to work so that it will make a copy of each of the selected dice tokens, then move only one of each token (either the original or the new copy, I don't care which) up to a specific area of the map? Thanks again for everyone's help!
1674676684
timmaugh
Pro
API Scripter
Hmm... thinking that through, you should be able to do the 2 forselected calls. You have some number of tokens selected, you issue the command, and for each of them, it runs the first command. SelectManager doesn't do anything to the way Roll20 determines what tokens to include as "selected" in the message object... it just changes the message object once Roll20 parsers are done with it. So it isn't adding new tokens to the state of selected tokens on your VTT. That means when you get to the token-mod command and use forselected again, you're still iterating over the same selected tokens. If you don't care whether you move the original or the spawned copies, this should work. Your second version is just running into a race condition... where the forselected command line is going to generate a new message for each selected token. Those messages will slot into the queue as they are produced, but by that time the token-mod command is there, first. So it will go: forselected token-mod spawn spawn ...etc... So everything that will be copied has already moved by the time you get to the spawning. You might be running into the bug where commands are sometimes dropped from a multi-command ability/macro. To test that, run the Spawn command separately, and then change nothing about what is selected on your VTT. Once Spawn has finished, run the token-mod command separately (with everything still selected as it was before). If that works, you're having dropped messages. If that's your problem, you can use Plugger to help. (I have a better solution in the works, but this one is ready.) This version assumes that you want the SAME roll used for the "top" setting of the moved tokens: !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,0 --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1{^&eval($)} token-mod --set top|[[1d100/30+1]].valueg {$&select @^(selected.token_id)}{^&/eval} Aircoded that... but what that should do: Send everything as a single command (nothing gets dropped), where... !forselected(^)   => you will iterate over every token you have selected, and use the ^ as the deferral character to hide constructions, and... {^&eval($)}...{^&/eval}  => send the token-mod out as its own message, using $ as the deferral character .value   => because the token-mod messages will be separate from the spawn messages, they won't have the inline roll data, so we'll extract the value prior to having plugger send the outbound commands (in fact, prior to the spawn calls, even) {$&select ... }   => while the Spawn message will have a selected token (given to it by SelectManager and the forselected handle, token-mod is a different message. It won't have a selected token so we're going to give it one we'll select virtually. This formation is deferred using the Plugger deferral character, so it should "unmask" in the outbound TokenMod command executed for each token. @^(selected.token_id)  => This is deferred using the forselected deferral character, so it will "unmask" in the spawn command. Fetch (running in the spawn command) will return the token_id of the token SelectManager gave to this message.  So the {&select} statement is now filled with a token id when the token-mod command runs, and SelectManager jams referenced token into the selected tokens, which token-mod now sees. Alternate Solution If this doesn't turn out to be your problem (or doesn't work), there might be a way to do it with a single Spawn command that you use with forselected, but then you calculate an individual offset for the Spawn command using token-specific fetch calls. In other words, if you can get individualized offsets, you can have Spawn spawn the token in the correct location in the first place.
Thanks, Timmaugh!  I really appreciate the time and effort to type that all up.   First discovery:  I followed your suggestion to try running the two commands separately, and it turns out that the !forselected isn't working with my !token-mod command at all, even when running it by itself.   I 've tried both:  !forselected !token-mod --set top|[[1d100/30+1]]g   !forselected(^) !token-mod --set top|[[1d100/30+1]]g --ids @^(selected.token_id) and nothing happens for either of those - no error messages or anything.  So maybe I just have a syntax error with that, and it would work fine if I had the correct command?  I tried several variations but couldn't get it to work for any of them. Regardless, I next tried installing Plugger and using: !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,0 --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1{^&eval($)}token-mod --set top|[[1d100/30+1]].valueg {$&select @^(selected.token_id)}{^&/eval} If only one token is selected initially, it works fine.  If two or more tokens are initially selected, then it creates the correct number and types of copies, then it moves one of the original tokens to the top of the map, but leaves the other selected original tokens in their original spots.  The pattern of which original gets moved and which remains is odd - it alternates regardless of which one is selected first.  Not sure if that is a clue that might help troubleshoot it or not.  :) I really like your suggestion of using just the Spawn command and calculating the offset for it, but I haven't found a way to make that work.  Spawn throws an error that a "non-numeric offset is detected" whenever I try to get clever and insert a calculation in that spot.  There may be a trick to it that I don't know.  Again, thank you for your time and suggestions! Jim  
1674734964
David M.
Pro
API Scripter
What is the purpose of moving the source token up to the top of the map? Just for re-use later?  Since these are just dice tokens and presumable don't have statusmarkers, etc. that need to be preserved, what about the following: When spawning, delete the source token via --deleteSource|1 or something like --resizeSource|0,0,20,50 (for animated shrinking before deletion) Keep a static group of the dice tokens you want to re-use at the top of your map to copy whenever you need to re-use them But since I've never played Cortex and am not sure of how this is all being used during play, YMMV.  
1674741108

Edited 1674742149
Jim
Pro
In Cortex you have a bunch of traits, like attributes and skills and assets or complications, etc.  You have to build a dice pool by taking one (or more) dice from each of those types of traits and roll it.  But the rolls are often manipulated first, so dice can be stepped up or down to a larger or smaller size, doubled, etc.  Then you have a bit of strategy of taking the results and picking two (or more) dice to represent your action total, and other dice to represent effects, and any that roll a 1 cannot be used, etc.  So it is nice to be able to move them around and sort them, which is why we don't just roll them to the text chat log. We initially just had a pool of dice that anyone could pick from, and they'd consult a character sheet in Google Drive to decide which dice to use.  That was okay, but switching tabs to look up the character sheet was a little annoying.  It didn't flow as nicely as we wanted.  Next we tried building a character sheet right on the Roll20 map, laying out a grid for all the attributes and skills, etc. with the appropriate dice already assigned to each one.  But then anytime those dice were stepped up or down or doubled, you needed to reverse the process to get back to your "normal" attribute or skill dice, which meant everything took twice as long and it was easy for errors to creep in.   What we ended up doing is leaving an area at the top of the map to represent the pool you were building, then the player selects the dice they want to roll from the character sheet and click a token macro to copy those dice into that dice pool area at the top.  They can then make any dice changes to the copies up in the pool area, make the roll, and just delete them, while the actual dice down in the "character sheet section" remain at their correct values.   We're still experimenting with it, and maybe we'll find a better way to do it.  We're open to suggestions!  But for now, this is the best we've come up with, and it works well except that you have to copy one die at a time into the pool and can't just select multiple dice and hit the "Pool" button once.   EDIT:  And the reason for the random calculation in the token move is so two or more dice from the same "column" of the grid don't just get moved into the same hard-coded X,Y coordinate.  They still can land in the same spot occasionally if two dice from the same column happen to roll the same result for that random Y coordinate, but it is rare. 
1674744540
timmaugh
Pro
API Scripter
Here's a way to do the offset math using Fetch and MathOps. This equation just looks for a random destination between 0 and 100, and computes the offset from there, set up to generate a positive number: {&math @(selected.top)-randib(1,100)} Obviously, that's just an example, and would need to be changed according to the sort of number Spawn requires for the offset (positive = up? or positive = down?... pixels or map grid units?). As well, you could change the generation of the target destination...  I *think* what you're trying to do with the token-mod command is that you want to leave dice in particular positions (4 potential grid positions), effectively creating a step-function of placement. Not sure if it's important to be exactly on the grid markers, but if so you could do something like (pseudo-code): sourceTokenTop - ( topValueForTopRowPosition + ( randomIntegerBetween(1,5) * gridHeight) ) You can default/hard code the gridHeight to match your campaign, or agnosticize it by retrieving the value via Fetch. Hard-coding is obviously easier. Assuming a standard/default 70px grid size, and that your top position in your grouping box is 2 rows down from the top of your map (so where the grid's upper border is at 70px, but a 1-unit token placed there would have a top property of 105 since that measures to the center of the token), that there are 4 potential grid positions, and that Spawn requires a positive value for a vertical offset, a hard-coded version might look like: {&math @(selected.top) - (105 + (randib(1,5) * 70))} If Spawn didn't want decimals, you could round that just in case there is any oddness: {&math round(@(selected.top) - (105 + (randib(1,5) * 70)))} You might want to test that this correctly maths out the placements with that consideration of the token's center, but this should get you roughly where you're going.
Cool, that MathOps approach does work inside Spawn's offset parameter.  Here's the command I came up with, before adding !forselected: !Spawn --name|@(selected.character_name) --offset|0,{&math  ((35 + (randib(1,350))) - @(selected.top))/70 } --tokenProps|tint_color:@(selected.tint_color), name:@(selected.token_name), showname:@(selected.showname), isdrawing:1 That works just fine for one token.  Now when I add !forselected, it becomes this command: !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math  ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 That appears to work and do exactly what I want - it works for one token or multiple, and seems to work great!  ...  Except that ZeroFrame is now reporting an infinite loop in the chat log every time it runs.  I'll copy it below.  Despite the message in the chat log, it actually does exactly what I want.  I just don't know why that log message is happening.  Any ideas? Thanks again, Jim (From API): ZeroFrame Possible infinite loop detected. Check ZeroFrame log for more information.   (From API): ZeroFrame   LOOP 1 !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1   SELECTMANAGER   PLUGGER   FETCH   MATHOPS !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 NOTES: Expected a number, a variable, or parentheses   LOOP 2 !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1   SELECTMANAGER   PLUGGER   FETCH   MATHOPS !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 NOTES: Expected a number, a variable, or parentheses   LOOP 3 !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1   SELECTMANAGER   PLUGGER   FETCH   MATHOPS !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 NOTES: Expected a number, a variable, or parentheses   LOOP 4 !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1   SELECTMANAGER   PLUGGER   FETCH   MATHOPS !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 NOTES: Expected a number, a variable, or parentheses   LOOP 5 !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1   SELECTMANAGER   PLUGGER   FETCH   MATHOPS !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 NOTES: Expected a number, a variable, or parentheses   LOOP 6 !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1   SELECTMANAGER   PLUGGER   FETCH   MATHOPS !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1 NOTES: Expected a number, a variable, or parentheses   OUTRO FINAL MESSAGE !forselected(^) !Spawn --name|@^(selected.character_name) --offset|0,{&math ((35 + (randib(1,350))) - @^(selected.top))/70 } --tokenProps|tint_color:@^(selected.tint_color), name:@^(selected.token_name), showname:@^(selected.showname), isdrawing:1  
1674796130
timmaugh
Pro
API Scripter
Excellent! You're nearly there. I think the only problem is that you have to disguise the {&math...} trigger so that it waits until the messages forselected dispatches for each individual token. Basically, you're sending the first/main message, which is intended to be caught (at standard script timing) by SelectManager using the forselected handle. Before we get there, however, the other metascripts have at the message seeing if they need to do anything to it. MathOps sees the {&math...} construction and starts parsing. When it gets to the fetch construction (  @^(selected.top)  ), that doesn't follow the math syntax MathOps expects, so you get the message in that log you posted: Expected a number, a variable or parentheses. MathOps leaves the {&math...} construction alone, figuring it might just have to wait for Fetch, Muler, or another of the metascripts to replace the part of the equation that doesn't compute. But that means that on the next loop pass, it encounters the same bad equation... the Fetch construction is deferred so it will never resolve in the main message. It will only resolve once the deferral character is removed as a part of the message dispatched for each token. Your tokens are getting properly moved because after ZeroFrame reaches a certain number of passes where the command line hasn't changed, it figures it's in an infinite loop and just stops. That lets SelectManager's forselected handle pick up the message. Since the {&math...} construction was never parsed out of the message, and since the fetch construction will now be un-deferred, all of the syntax is there to process your tokens. The long and short of it is that you need to change this: {&math ... } to {^&math ... }
1674828979

Edited 1674829000
Jim
Pro
Awesome!  That did indeed solve it!  Thank you all so much for your help on this!  I've got all the functionality I was looking for, with the exception of the probable showname bug where !spawn always sets it to on if it is included in the --tokenProps.   I really appreciate everyone's help on sorting this out. Jim Just as an FYI:   In the process of testing this, I found a situation where the interactions between SelectManager, !spawn, and !forselected can throw an error that halts the API Sandbox.  Here's a simplified macro for reproducability: !forselected(^) !Spawn --name|@^(selected.character_name) --deleteSource|1 --offset|1,0 That should work with any token that has a character sheet assigned to it, and just deletes the current token while spawning a new one.   Here are the steps to reproduce the API Sandbox crash: If you call that macro without a token selected, you get a chat log from SelectManager with an empty string; presumably it was reporting that no token was selected, so SelectManager is detecting the lack of a selected token at this point and exits gracefully. Next if you select a valid character token and call the macro it works fine.  The new token is created but the original is deleted. Nothing is visibly selected on the screen. If you call the macro again, without having clicked on any other tokens, nothing happens in game, but the API Sandbox halts with this error: Your scripts are currently disabled due to an error that was detected. Please make appropriate changes to your script's code and click the "Save Script" button. We will then attempt to start running the scripts again.  More info...  If this script was installed from the Mod Library, you might find help in the Community API Forum. For reference, the error message generated was:  TypeError: Cannot read property 'get' of undefined TypeError: Cannot read property 'get' of undefined at forselected (apiscript.js:9701:160) at handleForSelected (apiscript.js:9817:9) at eval (eval at <anonymous> (/home/node/d20-api-server/api.js:168:1), <anonymous>:65:16) at Object.publish (eval at <anonymous> (/home/node/d20-api-server/api.js:168:1), <anonymous>:70:8) at /home/node/d20-api-server/api.js:1762: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) Presumably the fact that !spawn deleted the original token is not being recognized by SelectManager, since it no longer cleanly catches and logs that there is no token selected, and it instead allows !forselected to proceed as if the old deleted token still exists.  Then !forselected tries to process the !spawn command on an undefined token, and it throws that error and halts the sandbox.
Hi again, The scripts have been working well for my players, allowing them to build their dice pools more easily.  Thanks again for everyone's help. I'm looking ahead to ways to streamline my management of monster dice pools.  One idea is that I will set up the monster tokens with character sheets with some attributes that define how many dice of each type are in their main combat dice pools.  Then I could have a macro button that spawns the correct number and types of dice for the current pool. For example, if a given monster has a Threat of 3d8, and a specialty of d10, and a distinction of d8, and one asset of d6 and a second asset of d8, then it might have attributes like:     ThreatType=d8;  ThreatNum=3;  Specialty=d10;  Distinction=d8;  Asset1=d6;  Asset2=d8  And then I would have a macro that would parse the attributes, see which ones were not null and call the !spawn API as needed.  In that example, it would call !spawn 7 times to create 5d8s, 1d10, and 1d6 dice tokens.   I've considered using APILogic and putting it in a bunch of IF THEN blocks, which would look something like this pseudo-code {if ThreatNum >= 1} !spawn --name|ThreatType <other params> {endif} {if ThreatNum >= 2} !spawn --name|ThreatType < other   params> {endif} {if ThreatNum >= 3} !spawn --name|ThreatType < other   params> {endif} {if ThreatNum >= 4} !spawn --name|ThreatType < other   params> {endif} {if ThreatNum >= 5} !spawn --name|ThreatType < other   params> {endif} {if Specialty != ""} !spawn --name|Specialty <other params> {endif} {if Distinction != ""} !spawn --name|Distinction <other params> {endif} {if Asset1 != ""} !spawn --name|Asset1 <other params> {endif} {if Asset2 != '""} !spawn --name|Asset2 <other params> {endif} I played around with that a little, but couldn't get the syntax to work.   !Spawn --name|@(selected.ThreatType) --offset|0,1  Spawn works fine using an attribute in the parameters; if ThreatType is D6 then it spawns a D6 token. !{&if @(selected.ThreatPool) >= 2 } ThreatPool is @(selected.ThreatPool) so in IF block {&else} ThreatPool is @(selected.ThreatPool) so in ELSE {& end} {& simple} The IF/ELSE block also works fine.  If the ThreatPool is 2 or more, it outputs a line that shows that it is in the IF block so the comparison is working. !{&if @(selected.ThreatPool) >= 2 } !Spawn --name|@(selected.ThreatType) --offset|0,2 {& end} But when I put them together, that command fails and does nothing.   So then I started thinking of alternatives.  I considered writing a short script of my own that would handle the looping and logic structures, then call the Spawn api script to do the actual token creation, but I'm not sure how I'd call someone else's script from inside my own.  I don't know the namespace and overall structure of the API system well enough.  Is that a viable choice?   How would you recommend approaching this? Thanks for reading! Jim
1675130289
timmaugh
Pro
API Scripter
You have an extra space in there, Jim... When the IF block resolves and is removed from the line, you have to look at what is left. In many cases, an extra space won't matter, but when it comes to leaving the message in a state so that Spawn will know to pick it up, you have to make sure that the message *starts*: !Spawn It looks like you have ZeroFrame installed, too, so it won't matter if the message gets to: !!Spawn (two exclamation points) because ZeroFrame is by default configured to leave any bangsy message with only a single bang. However, you can just do it yourself by dropping the exclamation point before Spawn and butting "Spawn" up against the IF block... that would be: !{&if @(selected.ThreatPool) >= 2 }Spawn --name|@(selected.ThreatType) --offset|0,2 {& end} Give that a try and see if it works better for you. As far as looping, you can do that with a scriptcard or with Muler. I just did something similar for someone else using Muler. If you want an example of Muler, post back. The basic idea would be having your call to Spawn be a muled variable which runs a Muler-get statement inside an if block. With every get, you increment a counter (in a mule); the IF block checks to see if the counter is less than your target number of calls. If it is, it retrieves the same Spawn-call-variable (ie, itself). So the counter gets incremented, and we get a new copy of the line again... which checks the counter, etc., etc.
Thanks, yes, it was the extra space causing that issue.  The IF blocks are now working, but I'm running into inconsistent results with it sometimes dropping command lines.  I have the initial spawn, then five additional IF blocks to see if the ThreatPool is at least 2, 3, 4, 5, or 6.  If I set ThreatPool to 6, it should create 6 new dice tokens, and it does sometimes.  But more often it creates 3 or 4, and skips the rest.  Which ones get skipped varies.  It might create dice 1-3, and skip 4-6, or it might do 1-2, and then 5-6, skipping 3 and 4, etc.   Would the Muler approach help that, or is there anything else I could do?  If it would, then I'd definitely appreciate the example of the Muler loop. I have seen other forum threads where the issue of dropped/skipped command lines has been brought up.  The discussion that comes to mind (since I just read it earlier tonight) was to bring all the commands onto one line, but I'm not sure if that would work for this.   Thanks, Jim
1675181683

Edited 1675181843
timmaugh
Pro
API Scripter
Yeah, that's the dropped-line bug you're running into, there. Muler and Plugger would get you past that bug (I have a more elegant solution than Plugger for multi-command-line macros, but for now this would work. Here is the basic setup for a simplified usage, just to demonstrate things: Make sure you have the latest Muler and Plugger from the 1-click. Create (or designate) a character to hold the mule abilities. Either grant "controlledby" rights to the users that will need to use the mule (might be just you, and if you're the gm you can skip this step), or configure Muler so that players don't need controlling rights to access mules: !mulerconfig -playersneedcontrol I'll call my mule character TableMule . On TableMule , create an ability named d8Spawner and format it like this: command={\&if get.TableMule.d8Spawner.count/get < get.TableMule.d8Spawner.needed/get }The command line. get\\.TableMule.d8Spawner.command/get set\.TableMule.d8Spawner.count = {\& math get.TableMule.d8Spawner.count/get + 1}/set{\&end} count=0 needed=0 You would need a similar mule for each of your die-types, or you could alter the variable names so that they were die-specific (ie, commandd8, countd8, neededd8, commandd10, countd10, neededd10, etc.). For now, we'll just keep it simple. Create another ability (anywhere) and use this command line: !get\.TableMule.@{selected|ThreatType}Spawner.command/get set.TableMule.@{selected|ThreatType}Spawner.count = 0/set set.TableMule.@{selected|ThreatType}Spawner.needed = @{selected|ThreatPool}/set {&simple} Select a token that represents a character with a ThreatType  attribute set to "d8" and a ThreatPool  of some value greater than 0, and run that command. You should get an output made up of a number of "The command line. " equal to the ThreatPool . Expanding that Simple Example 1. Fetch In that example, I used roll20 sheet-attribute retrieval instead of Fetch, but if you needed to iterate over many selected tokens, you'd have to use forselected (SelectManager) and change those roll20 attribute retrievals to deferred Fetch formations. Also, if you need to do the offset trick for your Spawn command, you'll still rely on the Fetch constructions to refer to the selected token's top/left. SelectManager runs *before* Fetch in the ZeroFrame order (by default), so the selected token should be restored before Fetch goes to get those values. 2. Deferrals and the Order of Operations for ZeroFrame The current rate of deferral is based on the default order of the metascripts: (You can get that by running !0   ) ...meaning that when the initial get runs to go get the specified command line, we want the inital set statements to have run (setting the needed and count variables)... so we defer the get by 1-cycle (ZeroFrame deferral = backslash). Once the get statement resolves, we now have a command line that contains an IF statement from APILogic:  {\&if get.TableMule.d8Spawner.count/get < get.TableMule.d8Spawner.needed/get }The command line. get\\.TableMule.d8Spawner.command/get set\.TableMule.d8Spawner.count = {\& math get.TableMule.d8Spawner.count/get + 1}/set{\&end} We just left the get portion of the ZeroFrame loop, but we've left an IF block in the command line... and APILogic is yet-to-come in this pass of the loop. Since that IF block relies on a pair of get statements, we have to defer the IF resolution 1-cycle to give the get statements time to resolve (in the next loop). Similarly, the math operation relies on a get statement... and since the get statement here won't resolve until that next loop (when the get statements in the IF block would also resolve), we also have to defer the math operation by 1-cycle. The set operation relies on the deferred math operation. Since the math is deferred 1-cycle, the set statement has to be deferred, as well. Since set operations happen AFTER math operations, it will only take 1 deferral. The set will resolve in the same loop pass when the IF block resolves... but since it runs BEFORE APILogic, the variable should be incremented before APILogic evaluates it. 3. Plugger All of that deferral matters because your actual command line won't be "The command line. " It will be a Spawn command that you're going to wrap in Plugger EVAL tags: command={\& if ... }{\\&eval}Spawn ... {\\&/eval} get\\.TableMule..... You can see I want the EVAL tags deferred 1-cycle past the IF block; this is because APILogic is the last script to run in the ZeroFrame cycle, and it's only after it finishes evaluating things that I want to run the outbound command to Spawn. That is, only if the conditional check passes do I want the EVAL block to survive in the command line, so I don't want Plugger to detect the EVAL block until after the conditional runs. Plugger EVAL tags take a custom deferral character the same way forselected does: enclosing it in parentheses right after the eval ... {&eval(^)}...{&/eval} ...should you need to defer the resolution of something until you're in the outbound command Plugger generates. 4. Simple Once you convert to actual calls to Spawn, you'll want to remove the {&simple} tag, obviously. 5. Storing/Fetching the Command Finally, if you do intend to issue this command as a part of a forselected iteration over selected tokens (first of all, that's pretty cool: one message spawns a message-per-token, each of which spawn a message per EVAL block, each of which then spawns a token -- literally), house this command line in an ability macro which you can retrieve via a deferred-Fetch call: !forselected(^) @^(MonsterDiceSetupCharacter.SetupForSelected) That way, you don't let ZeroFrame detect any work to be done until the individual commands are issued. Otherwise you'd have to manage ZeroFrame deferrals *and* forselected deferral in the same place. That can get messy. Required Scripts Spawn, ZeroFrame, Fetch, SelectManager, MathOps, Muler, Plugger, APILogic
1675187397

Edited 1675187881
Jim
Pro
First, that's amazing, timmaugh.  Both the scripts, and the support you provide.   Unfortunately, I do have a follow up question.  I think I set everything up as described for the test, still using just the text "The command line."  But when I run it, it gets into an infinite loop and shuts down the API sandbox when it hits 2704 times through the loop.  Here is the updated content of the d8Spawner ability on the mule after running the job.   command={\&if get.CortexMacros.d8Spawner.count/get < get.CortexMacros.d8Spawner.needed/get }The command line. get\\.CortexMacros.d8Spawner.command/get set\.CortexMacros.d8Spawner.count = {\& math get.CortexMacros.d8Spawner.count/get + 1}/set{\&end} count=2704 needed=3 The variable "needed" is 3, which is what I set it to in the other token.  But I watched the content of the ability as the variable "count" was incremented until it hit 2704 and the sandbox shutdown.  The syntax looks correct to me, but I'm wondering if there is something in the way the pieces are deferred that is incorrect.   Any ideas? EDIT:  I went back to the other token where it calls the d8Spawner and changed it to set the initial count=5 instead of 0.  I expected that the loop would never run since the initial IF should have been false, but it still went into an infinite loop. So it seems like the issue is in the IF comparison itself, but I don't see any errors.  I wondered if I had a typo in the get for the needed variable, but it looks correct to me... EDIT 2:  Would the order I load the API scripts matter?  I've seen other posts were that was brought up as sometimes causing issues, and I have just been adding additional scripts as needed, so they might be in a sub-optimal order.
1675192717
timmaugh
Pro
API Scripter
To answer your question, as long as you have ZeroFrame installed, the installation order won't matter -- the rest of the metascripts look for ZeroFrame, and if it's found they wait to get the message from ZeroFrame (in the order ZeroFrame manages) rather than from the Roll20 spigot. You can confirm that your order is the same as mine (still the default) by running: !0 ...and comparing the output to mine, pictured above. As to why you're getting the infinite loop... can you share your triggering command line? If nothing jumps out at me I will use those command lines and mock up in my game what you have going on in yours.
1675203061

Edited 1675203125
Jim
Pro
Alright, the last issue was user error:  at some point when troubleshooting something earlier, I had disabled APILogic.  So that is fixed.  On to the next... My API order is a little different, with Plugger and Fetch both at 50 and Plugger listed first.  I don't recall doing anything to change that.  Is that an issue? (From API): ZeroFrame 20 selectmanager selectmanager, sm 25 mulerget mulerget, get, muleget, muleload, load 50 plugger plugger, eval, plug 50 fetch fetch 55 mathops mathops, math 65 mulerset mulerset, set, muleset 70 apilogic apilogic, apil, logic   And now it is working right up until I add the EVAL tags.  Here's what I have for the two macros: On the token:   !get\.CortexMacros.@{selected|ThreatDie}Spawner.command/get set.CortexMacros.@{selected|ThreatDie}Spawner.count = 0/set set.CortexMacros.@{selected|ThreatDie}Spawner.needed = @(selected.bar1_value[1])/set  On the mule: command={\&if get.CortexMacros.d8Spawner.count/get < 10 && get.CortexMacros.d8Spawner.count/get < get.CortexMacros.d8Spawner.needed/get }{\\&eval}Spawn --name|d8 --offset|0,-1 --tokenProps|tint_color:#ff0000, name:temp, showname:1, isdrawing:1{\\&/eval} get\\.CortexMacros.d8Spawner.command/get set\.CortexMacros.d8Spawner.count = {\& math get.CortexMacros.d8Spawner.count/get + 1}/set{\&end} count=5 needed=4 Running that causes an error in the mod console, shown below.  If I take out the EVAL tags but leave the Spawn, it will run but only creates one token regardless of how many are in the loop.   For reference, the error message generated was:  TypeError: Cannot read property 'who' of undefined TypeError: Cannot read property 'who' of undefined at handleInput (apiscript.js:7131:43) at eval (eval at <anonymous> (/home/node/d20-api-server/api.js:168:1), <anonymous>:65:16) at Object.publish (eval at <anonymous> (/home/node/d20-api-server/api.js:168:1), <anonymous>:70:8) at /home/node/d20-api-server/api.js:1762: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
I've been troubleshooting this further, and wanted to confirm that Plugger was working for other things.  This command works fine, so I think that is good. ![[1d10]]{&eval}getDiceByVal( $[[0]] >=1 included total){&/eval} {& simple} So then I tried the simplest version of Spawn, and I can't get this to work, it always throws errors in the mod console.   !{&eval}Spawn --name|d8 --offset|0,-1{&/eval} I thought it might be an issue with Spawn not getting passed the selected token, or something like that, but Spawn gives an error message if it is called without a selected token, and even that doesn't happen when {eval} is around it. Anyway, before spending more time on this, I want to confirm whether this approach is going to solve the bigger goal.  The macro button has to create multiple threat dice (that is where the loop we've been working on comes in), but it will also need to spawn another 3 to 5 additional single dice that won't be part of a loop.  If those commands are not part of a loop, will they still be dropped randomly by Roll20?  Tim, you mentioned another way to handle multi-command-line macros.  Is that something that is available in the script library, or something you are working on privately?  
1675262332
timmaugh
Pro
API Scripter
The multi-command line script will be available in the script library; it's pretty close, just working out a couple of kinks. =D As for your error, your stack says "eval", but that's actually just part of the javascript sandbox. The error is in the handleInput that is at line 7131. When your sandbox boots up, many scripts these days will report their line offset so that error line number will be more helpful. If you look at a freshly booted sandbox and read the logged messages from scripts can you see where it reports an "offset" or a starting line number, and if so... which script might encompass line 7131? (If not, I have a script that can help pinpoint that.) The type of error you are getting (where handleInput is looking for "who") tells me that the error might be in that the plugger-dispatched messages are script-generated messages. If I'm right, SelectManager might be able to help. Check your SelectManager config by running: !smconfig If it isn't already giving back the "who" and "playerid" properties, tell it to do so: !smconfig +who +playerid Then try your command again and report back... with your shield, or on it. =D As for if this will ultimately solve the dropped-line problem given that you'll have other one-off token spawns... yes, it should. The script I'm working on will treat all of it as a single command at first, then dispatch the individual lines independently. The loop will start a loop, and your one-offs will... um... one-off.
That was the issue.  The !smconfig showed that everything except "selected" was disabled.  I enabled who and playerid, and the eval for spawn is now working.  Do you recommend turning on playerscanids too, or only if required by some future use case?
1675273125
timmaugh
Pro
API Scripter
Good news! The only reason to have playerscanids or who disabled is if you have scripts that dispatch commands based on campaign activity and which also differentiate between whether a command is issued via the script moderator (ie, script-generated) vs issued by a player because the script will take different action. The potential existence of such a script was mentioned to me when I was writing SelectManager, so I left those two settings disabled by default, but I have yet to run into a script for which they cause a problem. I would say you're good to turn them on and then just watch for unexpected behavior from scripts that might automatically fire when some campaign event passes. (And, btw, if you find one like that, let me know so I can keep track of it!) I'll post to the forums when the multi-command line batching script is available, which should take you the rest of the way over the finish line.
I'm sure this has been done but is there any way to have the spawned token match the size of the source token, i tried using fetch mod but the height/width attributes it can get give the size in pixels where spawn needs them in squares and as far as i can tell inline sums don't work with fetch so i can't just divide the result by 70: !Spawn --name|Mimic --side|10 --size|[[@(selected.height)/70]],[[@(selected.height)/70]] --rotation|@(selected.rotation) error: SyntaxError: Expected "(", ".", "[", "abs(", "ceil(", "d", "floor(", "round(", "t", "{", [ |\t], [+|\-] or [0-9] but "@" found.
1675350948
timmaugh
Pro
API Scripter
Yep... Fetch runs after the inline rolls, so you have to work around that. There are 2 options... 1. Install ZeroFrame and defer those rolls: [\][\]@(selected.height)/70\]\] The number of backslashes is the number of deferrals (cycles) to wait. Since you only have to wait for Fetch, which isn't waiting on anything else, that's 1 cycle... so 1 backslash. 2. Install MathOps  (and possibly ZeroFrame), and use: {&math @(selected.height)/70 } The reason you might need ZeroFrame is just to ensure order. Scripts typically run in the order in which they are installed (even metascripts like Fetch and MathOps; they just run BEFORE standard scripts), so if you install Fetch first (as you have) and then install MathOps, MathOps will run after Fetch, and everything will be right with the world: Fetch will resolve the height property, and MathOps will do your math. However... ...I have seen scripts "move" in the installation order -- even without user interaction. Script B that had been installed after Script A suddenly reports as being installed *before* Script A. To make sure things work -- and always work -- you can install ZeroFrame, which will take charge of all the metascripts and organize them into "cycles" or "loops" that will work until there is no more meta-work to be done... then it will release the message to standard scripts downstream. With ZeroFrame, the default configuration is that Fetch will resolve before MathOps.
Hi Tim, I have everything working pretty close to how I want it.  I've got the Muler/Plugger/Spawn combo working, and it is creating all the necessary tokens without dropping any.  Thank you again for all your help!   I've reorganized it a bit, adding in a Chat Menu and Handout Buttons to give easy access to any of the die or token variations.  It works really well.   The one thing I'm still struggling with is the Chat Menu.  By itself, it works perfectly but it sends the menu to everyone in chat.  It also works if I use a whisper to the GM, but I don't really want to see the menu spam every time a player wants to use it, so I'm looking for a way to only whisper to whoever clicks the button.   I couldn't find a way to do that natively in Roll20, which I thought was odd.  I did find the  Whisper Self scriptlet , and I've confirmed that  it works great for some other chat menus that I'm using for other things, but it doesn't work with the Muler/Plugger/Spawn chat buttons (instead of just creating the buttons in chat, it actually calls the macros that the buttons should link to, creating one token for each button, and then creates buttons with truncated URLs that don't work).  Do you know of a way that would let me whisper to whoever is activating the chat menu?  I'm really surprised there isn't a "/w self" or "/w me".   If it matters, here is an abbreviated version of the action that creates the menu.  It creates the first row of 5 buttons; the actual menu has about 10 rows of 5 each.   /w gm &{template:default} {{name=Dice Menu }} {{Stress=[d4](!set.CortexMacros.Spawner.dietype=d4/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) [d6](!set.CortexMacros.Spawner.dietype=d6/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) [d8](!set.CortexMacros.Spawner.dietype=d8/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) [d10](!set.CortexMacros.Spawner.dietype=d10/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) [d12](!set.CortexMacros.Spawner.dietype=d12/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) }}
Hi, I've been using the api script Spawn Default Token to make copies of an existing token, including a variety of token properties such as its name, tint color, etc.  I'm now trying to also copy the tooltip text when spawning the new token.  This works fine for a short tooltip without any punctuation.  However, if I put in a sentence with commas it throws errors.   An example of the command would be:   !Spawn --name|@(selected.character_name) --tokenProps|tint_color:@(selected.tint_color), name:@(selected.token_name), showname:@(selected.showname), isdrawing:1, tooltip:@(selected.tooltip) That works fine if the tooltip is "blah blah blah", but if the tooltip is "blah, blah, blah" it will throw errors in the chat log saying:   (From SpawnAPI):  Invalid token attribute requested (blah) (From SpawnAPI):  Invalid token attribute requested (blah") I also tried using HTML entities like &#44 instead of the comma but they just get treated as plain text in the tooltip so that isn't a useful option.   Is there any way to safely copy tooltips with punctuation when using Spawn Default Token?   Thanks, Jim
1677522187
David M.
Pro
API Scripter
Jim, not currently. The tokenProps command accepts multiple properties by comma-delimiting, so the "extra" commas are mucking things up. I'll have to think about how to handle that differently, like an alternate delimiter or something.
Thanks, David.  Just a thought - what about allowing double quotes around the property value, or something like that, to signify a string that shouldn't be parsed?
Hello David, I am not particularly code-minded, but I have been trying my hand at using API scripts and macros for my own campaign to generate basic randomized battlemaps, as well as other things. I also apologize if my question has already been answered or is not particularly interesting... So, my question for you is this: is it possible to call tokens from a deck that has images within it and apply them to the board as drawings? Or would I have to work with a separate API? Currently I am trying to use DefaultTokenSpawner to randomly grab from a pool of tokens generated by an already-existing deck and then place them along random spots of the battle mat. The biggest hangup that I have at the moment is that I cannot seem to figure out how to call from the deck itself without listing every token that could possibly come from it by name. Is there a way to do this? 
1678793516
David M.
Pro
API Scripter
Garrett, by "deck" I'm assuming you mean a rollable table. The Spawn script can't do anything with card decks. Spawnin a random side of a rollable table token is pretty simple.  Create a rollable table token . Create a character sheet whose default token is that rollable table token. Spawn command will be something like this: !Spawn {{   --name|charName   --side|rand }} Creating and placing random terrain can be a bit more complicated. Y ou may want to check out this post I made regarding a random terrain generator. There's a bit of setup involved, but it uses the mods Spawn, Scriptcards, and SelectManager to randomly distribute various terrain elements on the map. 
Hello David, Thank you for your response. I very much appreciate the input. I felt that I was definitely wasting my time trying to figure it out, but it relieves me greatly to know that there is in fact a way to do this; even if it doesn't necessarily suit my (admittedly specific) purposes. Thank you so much!
Hi David! I don't suppose anyone has found a fix or workaround for the vision bug reported last year? <a href="https://app.roll20.net/forum/permalink/10546375/" rel="nofollow">https://app.roll20.net/forum/permalink/10546375/</a> Tokens spawned with darkvision still seem to see light sources as black circles, but toggling the vision setting off and on again with TokenMod seems to work. Just curious if there was a solution to prevent it yet.
1681385358
David M.
Pro
API Scripter
Persephone, I'll try to take a look at it this evening. Could you share the exact Spawn syntax you are using for setting the vision? The fact that token-mod is working gives me hope, but I'll have to see what Aaron is doing differently.&nbsp;
1681406453

Edited 1681406471
I was spawning my character token with just its default properties, so this: !Spawn {{ --name| Knock Knock --offset| 0,1 }} But I also just tried using the tokenProps arg to match the properties of my token just in case: !Spawn {{ --name| Knock Knock --offset| 0,1 --tokenProps|has_bright_light_vision:on,has_night_vision:on,night_vision_distance:60,night_vision_effect:Nocturnal }} The result didn't change, though :/ Here's how the token sees a dancing light (dim 10) Global lighting is off. When turning it on, only the brightest setting makes the spot visible for the token. But if I lower the global brightness at all the token loses all visibility. Using this TokenMod command twice on the token fixes it: !token-mod --flip has_bright_light_vision Hope this info helps! We can just drag our tokens out normally for now, I just noticed this when trying to set up a Party Spawn macro for our GM.
1681409311
David M.
Pro
API Scripter
I can't test until I get home, but does it happen to work if you change "on" to "true"? Or "1"? E.g. --tokenProps|has_bright_light_vision:true,has_night_vision:true,night_vision_distance:60,night_vision_effect:Nocturnal
No, all of those have the same results.
1681416221
timmaugh
Pro
API Scripter
Jim said: Hi Tim, I have everything working pretty close to how I want it.&nbsp; ...SNIP... The one thing I'm still struggling with is the Chat Menu.&nbsp; ...SNIP... Do you know of a way that would let me whisper to whoever is activating the chat menu?&nbsp; I'm really surprised there isn't a "/w self" or "/w me".&nbsp;&nbsp; If it matters, here is an abbreviated version of the action that creates the menu.&nbsp; It creates the first row of 5 buttons; the actual menu has about 10 rows of 5 each.&nbsp;&nbsp; /w gm &amp;{template:default} {{name=Dice Menu }} {{Stress=[d4](!set.CortexMacros.Spawner.dietype=d4/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) [d6](!set.CortexMacros.Spawner.dietype=d6/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) [d8](!set.CortexMacros.Spawner.dietype=d8/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) [d10](!set.CortexMacros.Spawner.dietype=d10/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) [d12](!set.CortexMacros.Spawner.dietype=d12/set set.CortexMacros.Spawner.color=#ff0000/set set.CortexMacros.Spawner.name=Stress/set get\.CortexMacros.Spawner.command/get) }} Sorry I missed this way back when, Jim! There is a Fetch construction for just what you want: ...for a speaker that is a player =&gt; @(speaker.display_name) ...for a speaker that is a character&nbsp;=&gt; @(speaker.character_name) Which can make it difficult because you don't want to have 2 different macros for whether you're speaking as a character or player. That's why I do it as a combo fetch call, using one structure as the default value for the other: @(speaker.display_name[@(speaker.character_name)]) That way no matter who you speak as, you should get the correct name. =D
1681438588
David M.
Pro
API Scripter
Persephone, after a bit of troubleshooting and discussion with some of the mod veterans, it seems that some UDL vision settings still do not take effect upon token creation via a mod script. You'll find that after you spawn your token, if you just open the token settings window and save without changing anything, the token vision will suddenly start working according to its settings. This type of behavior has been seen before and it seemed like it was fixed for at least some situations, but it seems to be back to the old non-working behavior. I have a lead on a hack fix for this current Roll20 limitation, but am running into a bit of an issue implementing it at the moment. If I get anything working, I'll let you know. Also, I believe the Devs are aware of the issue, so hopefully we'll see an actual fix sometime soon.&nbsp;
Thanks, Tim!&nbsp; I'll give it a try.
Hello there! I've had a blast trying out this script, so thanks a bunch for your work! I ran into an odd thing and would love to know if it's a user error/my goof. If I try to spawn the default token of a creature from a premade module I've purchased, it does not seem to recognize the image as being in my library. If I myself drag a creature from the compendium to the module, that creature works just fine with the exact same code. I can't seem to figure out why I can't with a paid module's creatures, though. Would it be because the module as a whole has the "marketplace" tag that the API doesn't like?
Hi Rayne! Yes, sadly scripts cannot interact with marketplace images. The only workaround I've had luck with is saving the token images from the module to my PC, then re-uploading them to my library and making new default tokens for those creatures with the re-uploaded images.
Hey Persephone, I appreciate the quick answer! I'm glad (but dismayed) that I was on the right track. At least now I know it wasn't because I'm a dummy at copy-pasting. As for your work-around, I assume the way you suggest is to get the token on the map, hit Z, and right click Save As, correct? Just checking on the off-chance there was a method I was unfamiliar with.
1682166014
David M.
Pro
API Scripter
Yep, that should do it, Rayne. I'm not aware of an easier method,&nbsp; unfortunately.&nbsp;
Thank you both for the guidance, and I hope you each have a lovely day!
Hello. I just recently upgraded to a Pro account and learning about API. I tried the following script, and it summons the token perfectly, but it does not delete the temporary "spawn" token. Not a huge issue, but annoying. Is there anything wrong with my syntax or is that command just not working? !Spawn {{--name|PLAYER1 --offset|0,0 --bar1|@{PLAYER1|hp}/@{PLAYER1|hp|max} KeepLink --bar2|@{PLAYER1|ac} KeepLink}} --deleteSource|1
1682731790
timmaugh
Pro
API Scripter
The double right braces end your single-line input. Put your delete command inside those and it should work. =D