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

Demystifying Using Scripts

1621545354

Edited 1623162358
timmaugh
Pro
API Scripter
A question came up in another thread about why certain calls worked and others didn't, and how to know when one might be required, so I decided to create this thread as a place to drop general knowledge that's just good for everyone to know (especially new users of the API). The first couple of posts will be specific to the questions raised in the other thread, but people should feel free to contribute pieces of knowledge that they found useful or helped them as they were learning to use APIs. Once we get enough good information, we can look at transferring it to the wiki. For now... the answers to the questions that came up already... Embedding Scripts in other Script Calls There are 2 ways to think about/implement this. First, every chat statement or macro calling an API produces a message that is handed from one script to the next in (roughly) the order those scripts are installed. (The meta-scripts are an exception in that they use a trick to get to the head of the line). Each script looks at the message to see if it needs to do anything with it. Typically, this is by examining the handle (like,  !scriptcard ), but it doesn't have to be that. The meta-scripts get in, handle their business with the command line or message object (like the selected tokens), and then get out of the way so that the next script can pick up the message -- so that eventually that message reaches ScriptCards, TokenMod, AlterBars, or whatever the intended script is. The second way to think about it is that some scripts give you the capacity to launch a new call to another message. This would be a case where message A launches message B (with each of them going through the every script hand-off described above). Not every script allows this -- ScriptCards is one, Plugger is another, the  forselected  handle of SelectManager is another. You have to use the appropriate syntax for each to trigger the behavior. ScriptCards  requires the argument to begin: --@ Then there is other syntax around not interfering with the way ScriptCards breaks down the command line (replacing double hyphens that need to appear in the downstream AlterBars call with an underscore, like you do before target, bar, amount, and show). forselected  just requires you to prepend the line with the forselected handle (though there are more options): !forselected spawn ... Plugger , as a meta-script, just wants you to wrap the downstream call: {& eval} !somescript ... {& /eval} How far can this go? Given the right syntax, you can nest these as deeply as you want. I've had situations where I've had scripts nested 4 or 5 deep.
1621545362
timmaugh
Pro
API Scripter
Need a Token? If your script needs to operate on/against a token, there are at least 3  built-in  ways to get it, as well as at least 1 from a meta-script. I. Select Tokens If your script can work with tokens you select on the tabletop, you can select the token on the board and call the script directly. The message will contain data on the set of selected tokens that lets the script take whatever actions it needs to. There are limitations to this, however, as you can only select tokens which you can control. Also, the set of selected tokens (also called an array) only exists for a user-generated message. If your script launches *another* script (message A launches message B), then the downstream script will have no array of selected tokens. !sciptcard {{ --@spawn|_name... There, ScriptCards is being call by a user, so it will know about the selected tokens. But when it calls Spawn, that is a script sending a new message, so no array of selected tokens survive. In effect, Spawn has no idea what tokens are selected. II. @{selected|token_id} If your script can accept token IDs in the command line, you could use the Roll20 construct  @{selected|token_id}  to get it. This has the benefit of resolving more immediately than situations where you might not retain the selected token array -- for instance, a downstream API call as just mentioned. TokenMod is a good example of this kind of ID handoff: !outerscript ##run|token-mod --ignore-selected --ids @{selected|token_id} ... Imagine that outerscript represents a script that can launch other scripts using the "run" argument; we will use that to launch TokenMod. As in the previous, outerscript, called by the user, knows about the selected tokens. TokenMod does not. That doesn't matter since we can pass a token id to it. We can use the  @{selected|token_id}  formation because that is resolved as a part of the Roll20 parsing, before the outerscript message is handed off to the API. The limitations of this are fairly obvious... only the first selected token's information is returned, no matter how many you have selected. Also, the script to which you intend to feed the results of this call must be setup to expect an id on the line. It doesn't help to tell a script about a token if the script's parser doesn't know to expect it, or what to do with it! III. @{target|Target|token_id} Much like the  @{selected...}  construction,  @{target...}  can obtain the token ID of a given token to provide it to the command line of a script that expects it. The advantage of this is that it doesn't have to be a token you control. You can target any token and return the token ID. Also to your advantage is that it resolves as quickly as the  @{selected...}  calls, above, meaning the data is there for downstream script calls (where one script has called another). While target assignments lend some flexibility, they come with several trade-offs. First, it requires an extra click for each unique targeting statement. Second, all  @{target...}  statements are read and parsed, and the user is prompted for each of them, whether or not each of them are required for a given usage. Third, a command that contains a targeting statement DOES NOT contain an array of selected tokens, even if it is user-generated (note, the  @{selected...}  formations of  II , above, do still work -- at least to return information about the first selected token). As an example: !scriptcard  {{ --@alter|_target|@{target|token_id} ... There, AlterBars is going to be handed the id of the targeted token, and it will receive it in the proper argument to let it know what to do with it. Note, however, that in this case the outer script (ScriptCard) will NOT have an array of selected tokens available to it. Sometimes this will not matter. Other times,  it very much will . IV. SelectManager (Meta Script) SelectManager runs as a meta-script, listening to every script call and for those that have an array of selected tokens, tracking those tokens. For an API-generated message (which doesn't have a selected array by default), it can, without user interaction, intercept the message and hand-off the array of selected tokens back to the message before that message reaches the intended-recipient API script. !outerscript ##run|token-mod --set name|Tacos With SelectManager installed, it tracks the tokens that are selected at the time that the OuterScript message is sent. When OuterScript calls TokenMod, that message doesn't have an array of selected tokens (as a script calling a script). SelectManager steps in first and quietly restores the token array to the message so they are there by the time TokenMod picks up the message. (It does not change anything about what is selected on the tabletop; it only tinkers with the message that the scripts are handing off, one to the next.) V. Virtual Selecting (SelectManager) SelectManager looks for syntax structures that tell it to build or add to the set of selected tokens. Since it is a meta-script, it runs before the intended recipient script gets the message: {& select ... } {& inject ... } Both take a comma-separated list of token names or IDs. The difference between them is that  {& select ... }  resets the selected tokens to nothing before adding the designated tokens, while  {& inject ... }  simply adds the designated tokens to the selected array. Using either of these formations can give back an array of selected tokens to a message that is lacking it. Note, these do not change what tokens are actually selected on the tabletop; they simply manipulate the message before it reaches the intended recipient script. This method can be a way to hard-code a set of selected tokens for a given macro (perhaps one that affects only the party members) so that the player doesn't actually need to select the tokens before running the command. They would be selected virtually for him/her automatically.