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 .
×
Advertisement Create a free account

[Script] SelectManager - update brings !forselected iteration and gives user new control to give selected tokens back to API-generated messages

1613591666

Edited 1614140284
timmaugh
Pro
API Scripter
SelectManager (New) FILES: Submitted to 1-click (until then, find it in my Repo ) SelectManager is a way to preserve the portions of a message that are present in a user-generated message but lost in an api-generated message (the selected tokens, who sent the message, and the playerid). The previous version of this script required the script developer to “opt-in” to use these features, but the new release (v0.0.5) also lets the user take control. With the new version, you can choose to “auto-insert” any or all of the properties into your downstream messages (those initiated by the API). EXAMPLE: You have a PowerCards macro that you build to launch a ChatSetAttr call. Because this is one script calling another, the ChatSetAttr message is generated by the API and the array of selected tokens is lost. Since ChatSetAttr works on the token currently selected, the call won’t work properly. Installing SelectManager, however, will make this situation work, even without a single change to your macro . !forselected Also in this version is a !forselected script handle that will take your existing call to another script and iterate over the selected tokens. So if you have a script that modifies a single token, or takes action based on the properties of a single token and you want to send that same call for multiple tokens at once, you would simply put forselected in front of the existing api handle. More on this, below. A Note on Timing SelectManager now uses the same 0-order trick as APILogic to shin-kick its way to the front of the line of scripts waiting to receive a message. This has been found to work with the vast majority of scripts currently available, but should you find a case where it doesn’t work you should remember that the best way to ensure SelectManager can get to the message-intended-for-another-script before that other script gets it (so that SelectManager can insert the necessary information) is to have it installed before other scripts in the game. !forselected - Iterate Over Selected Tokens Some scripts only process a single token at a time, forcing you to go through the process of selecting a new token, then re-issuing the command if you want to process more than one token in the same way. This can be tedious. With SelectManager installed, you can prepend that call to your other script with forselected and SelectManager will iterate over the selected tokens, sending the same call to the other script but cycling each selected token to hand only one off to each message. Just insert forselected (followed by a space) between the ! and the script handle of the downstream script. EXAMPLE: You want to use ChatSetAttr to set an attribute of a dozen tokens. The line might normally be: !set-attr --strmod|2 Using the forselected method, you would just insert the forselected text into the line: !forselected set-attr --strmod|2 If you have your dozen tokens selected, it will issue a dozen calls to ChatSetAttr, exactly the same except that the first call will see only the first token as selected, the second will see the second token selected, and so on. Special Constructions for Token or Character Sheet Info Anywhere you would use the @{selected|...} construction in a forselected command line, you can use at{selected|...} , instead. This is necessary since the Roll20 parser will eat an @{selected|token_id} structure and spit out the token ID of the first (but only first) selected token, for instance. To make sure these process for each selected token in turn, you should replace the @ version with the at version. SelectManager will detect these and retrieve the requested information for the current token in the cycle. See the Roll20 Wiki article on what information can be retrieved from a Token this way. Special Cases for token_id and token_name The most commonly utilized @{selected} requests are for the token_id and token_name, so to minimize how much you would have to alter your command line (beyond just inserting forselected at the start) to iterate over selected tokens, these two properties have been promoted to have special triggers. Use a + or a - immediately following the forselected to control whether to replace any reference to the result of an @{selected|token_id} call with the ID of the next token in the cycle. Use another + or - immediately following that to control whether to replace any result of the @{selected|token_name} call with the name of the next token in the cycle. These default to being “on,” meaning that references in the command line that match the token ID of the first selected token will be replaced with the token ID of the next in line. The same goes for the token name references. Therefore, you would typically use the handles to turn off this behavior. !forselected // replaces both (token ID and token name) !forselected+ // replaces both !forselected++ // replaces both !forselected- // token ID = OFF, token name = ON !forselected-+ // token ID = OFF, token name = ON !forselected+- // token ID = ON, token name = OFF !forselected-- // token ID = OFF, token name = OFF Use these in conjunction with the at{selected|...} formations to control what information is retrieved and when. For instance, if you have a character whose token ID should be referred to in one portion of the command line for every iteration of selected tokens (for instance, she is the “source” of some effect), but that character is also in your selected tokens, you don’t want to potentially automatically replace her ID in the command line: !somescript --source|-MZDKns0sna90an10 --target|@{selected|token_id} When you prepend that statement with forselected , you run the risk that the source character’s token ID will be replaced with every iteration (if she happened to be the FIRST token in the selected array). Instead, turn OFF the auto-replacement of the token ID, and use the at{selected} later in the line: !forselected- somescript --source|-MZDKns0sna90an10 --target|at{selected|token_id} User Opt-In (Auto-Insert) For macro/script calls other than forselected , SelectManager can auto-insert any or all of the three properties that are lost/changed when an API generates the message. Which of the properties SelectManager will auto-insert can be controlled from the chat interface and a few simple switches. Use the !smconfig script handle, followed by a space, and then a + (to turn on) or a - (to turn off) a given auto-insertion. !smconfig +selected // turns on selected auto-insertion !smconfig -who // turns off who auto-insertion !smconfig +playerid // turns on playerid auto-insertion These can be chained in a single line, handling any or all of them in one go: !smconfig +selected +who -playerid Once configured to auto-insert, these properties will be injected into any API-generated message with no other effort on your part. Run the simple command of  !smconfig  to see what you currently have configured for auto-insertion. SOMETHING TO BE AWARE OF:  As it comes, straight out of the box, SelectManager is only configured to auto-insert the selected tokens, and not the who or playerid properties. Before you enable the auto-insertion of the who and playerid properties of the message, think through the scripts in your game. There may be times another script needs to know that it was called by the API instead of a user. Certain scripts may trigger different activities based on who sent the message, and they may do different things if a user generates the call versus another script. For instance, if a script listens to events in the campaign and fires off a call to other scripts, those downstream scripts could very well be configured to know the difference and act accordingly. If you have a question whether a script behaves this way, ask the developer. Remember, even if you don’t tell SelectManager to auto-insert the properties of the message, the developer can still choose to opt-in and make use of SelectManager passively providing the information. Scripter Opt-In Whether or not the user has their installation configured to auto-insert stored pieces of the message, you can configure a script to get the information from SelectManager directly (typically we’re talking about a script’s author, though a user can update a manually-installed script, themselves, while they wait for the author to implement the SelectManager interface; for that sort of implementation, see Option 2: Use-If-Present Model ), below. Option 1: Make Your Script Dependent on SelectManager If you add SelectManager as a dependency to your script in the one-click, you can count on it being there when a user would utilize your script. In that case, you can access the library directly. Once your script has determined that the message should be received and handled by your script, assign whatever required properties to the various message objects. const handleInput = (msg) => { //... script tests for whether to pick up the message, then... if('API' === msg.playerid) { msg.selected = SelectManager.GetSelected(); msg.who = SelectManager.GetWho(); msg.playerid = SelectManager.GetPlayerID(); } //... script continues }; Alternately, instead of three lines, you could handle it with a single line, using deconstruction assignment: [msg.selected, msg.who, msg.playerid] = [SelectManager.GetSelected(), SelectManager.GetWho(), SelectManager.GetPlayerID();] Option 2: Use-If-Present Model If your script isn’t in the one-click or you’re unsure whether the SelectManager library will be installed, you can implement the script using just a few lines in your script’s definition. Note that if SelectManager is not installed for your user, then this method will not actually solve the problem of your script expecting, when called from the API, to be able to access the selected tokens (for example). However, this method allows your script to continue even if SelectManager is not installed, and if your user complains that the script breaks when they call it from another script, you can tell them the easy fix is to get SelectManager installed. The below example shows how to implement the GetSelected() function alone, though it can be expanded to include the others. Add the following two lines to the outer scope: let getSelected = () => {}; on('ready', () => { if(undefined !== typeof SelectManager) getSelected = () => SelectManager.GetSelected(); }); Next, locate the portion of the script that handles the on(‘chat:message’) event and include the following line immediately after the script tests the API handle to determine whether it should begin processing: if('API' === msg.playerid) msg.selected = getSelected(); (note that “msg” should be changed to match the name given to the message object in that procedure). The following example shows the same idea expanded to include all of the scripts: let getSelected = () => {}, getWho = () => {}, getPlayerID = () => {}; on('ready', () => { if(undefined !== typeof SelectManager) { getSelected = () => SelectManager.GetSelected(); getWho = () => SelectManager.GetWho(); getPlayerID = () => SelectManager.GetPlayerID(); } }); And then use those three functions ( getSelected() , getWho() , and getPlayerID() ) to access the data you need. Change Log v0.0.5 - Initial re-release, includes !forselected and 0-order interception allowing user control over auto-insert v0.0.6 ( link ) - Added multi-line downstream support for forselected (if the downstream command had line breaks, it would break, before); also added ability for api to call forselected, so that a third party script can launch a forselected iteration. v0.0.7 ( link ) - Added ability for forselected to work around a second exclamation point (such as: !forselected !set-attr...).
1613674033
David M.
Pro
API Scripter
Hey timmaugh, does SelectManager handle double bracket syntax for subordinate scripts? Doesn't seem to currently? !forselected {{!SomeScript ...a million cmds|args }} And before I get too far into the weeds, Is it true that if subordinate script "1" then called its own subordinate script "2", it would need to use forselected again? Or is this just folly and it would end up triggering the end of all creation? Without going into too many details, I was looking into having a Scriptcards macro fire off my Spawn script, both of which can have a pretty lengthy set of commands. The thought was to use forselected prior to the scriptcard, and then again for Spawn, all in a single ability. Not sure if that thought process would work. When I tried to just use forselected *within* scriptcards it didn't seem to work, even for simple examples. The guess is that the api had already eaten the selected info by the time forselected was called from within scriptcards.
1613677910
timmaugh
Pro
API Scripter
Hey, David... It doesn't handle the double-brackets natively (though it could be easily added), but I wonder if it needs to...? SelectManager just hands off everything downstream of the  forselected  handle to the iterative call... so I *think* all you need to do is to have the double-braces on the inside of the script that *would* parse them properly: !forselected SomeScript{{ ...a million cmds|args }} (You only need the initial exclamation point.) As for your suggested usage (ScriptCards launching a forselected), that's a great point that I will accommodate. Right now the script detects user-generated vs API generated, with the forselected test only happening on the user-generated side (so an api-generated call to forselected will just never be caught). But it's a pretty simple change to make it work (the stored selected, who, and playerid is obviously already available!)... I will break out the forselected test and subsequent code and let it be run from either the user- or api-gen side. On the api-gen side, it will just need to first restore those properties. Once I make that change, your usage case (ScriptCards calls forselected calls Spawn) *should* work. I should be able to get a new version out tonight, then you can give it a try!
1613682180
David M.
Pro
API Scripter
Thanks, Tim! Just tried a few variations using token-mod with very simple syntax with/without brackets. These all work !forselected token-mod --set statusmarkers|blue !forselected token-mod {{--set statusmarkers|blue}} !forselected token-mod {{ --set statusmarkers|blue }} This does not - seems like the line break is the culprit. !forselected token-mod {{ --set statusmarkers|blue }}  
1613682374
timmaugh
Pro
API Scripter
ok... I appreciate the leg work on that! I will add the parsing of the line breaks into the re-release asap.
1613712241

Edited 1613765465
timmaugh
Pro
API Scripter
Version 0.0.6 Released (Updated at Original Link and pending One-Click) Relase Date: Feb 19, 2021 Fixed : Double-braced command lines with line breaks will now work in forselected calls. Use the double-brace syntax within the sub-command forselected will iterate: !forselected token-mod {{ --lots of args --hoocha hoocha hoocha... lobster }} Fixed : API-generated calls to forselected should now be caught and executed appropriately. This addresses David's use case (above) of having ScriptCards issue a downstream command to forselected that would then issue a downstream command to Spawn. !scriptcard {{ --@forselected+|Spawn _name|imp _offset|1,0 --@forselected+|token-mod _set statusmarkers|blue|broken-shield }}
1613718108
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Very, very nice!
1613736337
David M.
Pro
API Scripter
Ahhhh. Works perfectly, thank you!!!
1613741572
Man, I have got to start using this script!
1613743646
timmaugh
Pro
API Scripter
Excellent! Let me know if there is any issue. One thing I realized is that it would be good for SelectManager to detect a second exclamation point and handle it. That way, you could have a macro or ability with a particular script call, and easily reference it from chat: For instance, you create a macro named SpawnStuff stored as:  !spawn {{ --things --that go bump }} Chat entered command: !forselected #SpawnStuff Then what reaches the parser is: !forselected !spawn {{ ... I'll build in the ability to detect that second exclamation and work around it. The whole idea is to give you an easy way to iterate an existing command over many tokens! =D
1613765685
timmaugh
Pro
API Scripter
Version 0.0.7 Released (Updated at Original Link and pending One-Click) Relase Date: Feb 19, 2021 Added : Ability to detect and work around a second exclamation point (one that follows the forselected api-handle): !forselected !spawn --things This is necessary if you expand a macro as the source of the downstream, iterated command, and that macro comes in with an exclamation point of its own. (This had to be the absolute SMALLEST update I've ever had to push that wasn't a typo correction. This amounted to adding 2 characters to a regex statement to add this functionality.) =D
1613824585
David M.
Pro
API Scripter
Handy!
Oooh, this might make the Delay script work for me finally! I'm eager to test it out.
1615535662
Hi, I recently stumbled over that great script while playing with token-mod. But I can't get one thing running: I have a multi-sided token with 10 different tokens for trees. I place an amount of them on the map and want to give them a random side. Therefore I tried: !forselected token-mod --set currentside|[[1d10]] This doesn't work. It seems to get stuck somehow, at least nothing happens. If I use a fix number instead of [[1d10]] it works. So there seems to be some issue with the inline roll. Does anybody have an idea how to fix that? Do I have to add some escape characters somewhere? Thanks in advance!
1615556314
timmaugh
Pro
API Scripter
Hi, Bernd... there's a short answer and a slightly longer answer. =D SHORT ANSWER: The short answer is that you might need to install APILogic , too (have it in the order of scripts *after* SelectManager), and then use one of the methods in this post  to defer that inline roll. SLIGHTLY LONGER ANSWER: The slightly longer answer is that SelectManager can iterate over your tokens, but by the time it sees the message, Roll20 has already parsed that inline roll down to a roll marker ($[[0]]). SelectManager doesn't do anything with that roll marker (and the inline roll associated with it) other than pass it along to token-mod, and token-mod should be capable of unpacking that roll marker to get the value within... so when you say "stuck" I'm not sure if you are saying that it doesn't unpack that roll marker to get the value. If that is the issue, let me know and I can investigate if there is some way SelectManager is getting in the way of that. That said, what I think you really want is for every token to have its own individual roll (to have the random side of the token set). Even if token-mod were to parse the roll marker and extract the value of the roll, that value would be the same for all tokens you were iterating over and you would end up with every tree being the same. By using one of the methods in the message I linked (above), APILogic will defer that roll and have it be new for each token.
1615558209

Edited 1615558824
timmaugh said: Hi, Bernd... there's a short answer and a slightly longer answer. =D SHORT ANSWER: The short answer is that you might need to install APILogic , too (have it in the order of scripts *after* SelectManager), and then use one of the methods in this post  to defer that inline roll. SLIGHTLY LONGER ANSWER: The slightly longer answer is that SelectManager can iterate over your tokens, but by the time it sees the message, Roll20 has already parsed that inline roll down to a roll marker ($[[0]]). SelectManager doesn't do anything with that roll marker (and the inline roll associated with it) other than pass it along to token-mod, and token-mod should be capable of unpacking that roll marker to get the value within... so when you say "stuck" I'm not sure if you are saying that it doesn't unpack that roll marker to get the value. If that is the issue, let me know and I can investigate if there is some way SelectManager is getting in the way of that. That said, what I think you really want is for every token to have its own individual roll (to have the random side of the token set). Even if token-mod were to parse the roll marker and extract the value of the roll, that value would be the same for all tokens you were iterating over and you would end up with every tree being the same. By using one of the methods in the message I linked (above), APILogic will defer that roll and have it be new for each token. Thanks for the quick reply. I'll try that! I also tried to accomplish that by using the 'Bulk Macro' option in the VTT Enhancer Suite, which works fine for Initiative etc. But that didn't work either. But I will give it a try with APILogic! :-) UPDATE: oh my. I am really blind. So much easier! Just creating many of these multi-sided tokens, select them and choose 'Multi-sided -> Random Side' from the context menu. That does the trick. No macro-voodoo necessary! :-)
1615612525
David M.
Pro
API Scripter
Oh cool, I didn't realize the context menu worked on multiple selected tokens like that!
1616356239
Having issues getting this to work with ChatSetAttr - the below one-liner works for what I want to a chieve for a single token: !setattr --sel --HP||(@{selected|level}*((@{selected|hitdietype}/2)+@{selected|constitution_mod})+@{selected|constitution_base}) --nocreate --evaluate However, attempting to use forselected with this: !forselected setattr --sel --HP||(at{selected|level}*((at{selected|hitdietype}/2)+at{selected|constitution_mod})+at{selected|constitution_base}) --nocreate --evaluate results in an error: Something went wrong with --evaluate for the character Chuck Testtoken. You were warned. The error message was: SyntaxError: Unexpected token '*'. Attribute HP left unchanged. Replacing that asterisk with a + results in [...] The error message was: SyntaxError: Invalid regular expression: missing /. Attribute HP left unchanged. Likewis,e replacing the /2 with +2 results in [...] The error message was: SyntaxError: Unexpected token ')'. Attribute HP left unchanged. I have SelectManager installed before APILogic, and have tried various permutations based off of the token-mod thread you linked above involving adding various escaped double brackets. Interstingly, surrounding that HP equasion with [\[...]\] and adding the null eval suffix seems to cause the API sandbox to bork with the message: [...] "ŦŦ APILPlugins01 v0.0.1, 2021/3/8 ŦŦ -- offset continues from APILogic}" "Starting webworker script..." TypeError: Cannot set property 'version' of undefined "Starting webworker script..." TypeError: Cannot set property 'version' of undefined Which continued to be thrown on sandbox restart until I set the macro text back. So, any idea on how I can get these custom HP equation macros up and running with SelectManager? I'm needing 8 of them. (long term plan is to set up a chat template to determine which one to run for the selected group of tokens.)
1616359902
timmaugh
Pro
API Scripter
I think you're running up against the limitation of the initial release of SelectManager, that it was firstly only concerned with the token properties, and you're looking for represented-character properties. That is the next stage of release, and part of a planned "Meta Toolbox" release... hopefully early this week (in time to get in the one-click merge). The functionality you need is coming! =D
1616364817
Fantastic, thank you.
1618937652
timmaugh
Pro
API Scripter
SelectManager (v1.0.0) FILE LOCATION:   META SCRIPT:  This script is a meta-script, and part of the ZeroFrame Meta Toolbox. It can be used on its own, or in conjunction with ZeroFrame and the other meta-toolbox scripts, to modify  other  scripts' API commands. ABSTRACT:  SelectManager helps API-generated messages remember what tokens are selected (they would otherwise not know), and lets you send commands to other scripts using the forselected handle. It also lets you "virtually" select tokens to ease interaction with the map. Introduction When a user sends a message to the API, the message contains an array of tokens that are selected. When the API sends a message to the API, those selected tokens are lost. Similarly, if a user includes an  @{target|...}  command, Roll20 doesn't include the selected tokens as a part of the message handed off to the API. This can be a problem for scripts built to expect at least one selected token in the message. SelectManager bridges this gap by remembering the tokens selected at the last point that a user message was selected, then restoring them to API-generated messages. This has been true in previous versions of SelectManager, and is still true, now. In fact, everything said about SelectManager in this thread is still true -- all of its existing functionality has been retained. This v1.0.0 release brings new functionality, including: The ability to plug into the ZeroFrame loop (if you have that script installed) The ability to virtually select (to replace) or inject (to add to) selected tokens. You can use this with Muler to create sets of selectable tokens you can easily add to a message without having to interact with the gameboard. A wider range of at(...) constructions to match Fetch retrievals for downstream token interaction, though #4 may obviate the need for this one... The ability to designate a custom escape character to defer downstream syntax detection (in other words, you tell SelectManager that for this forselected series of calls, SM should remove a particular character, which then allows syntax structures like a Fetch retrieval or a Muled variable retrieval, to be detected) Syntax Highlights No direct call interaction => SM can hand off tokens automatically !forselected spawn... => iterates a spawn script over the selected tokens !forselected !spawn... => same, including the exclamation point for downstream script !forselected(^) spawn @^(selected.token_id) => custom escape character for downstream script {& select Jax, Heretic} => replace selected tokens with comma-separated list (token ID, name, or near name) {& inject Jax} => Add Jax to the selected tokens, if not already included {& select get.TheParty} => Use a Muled variable "TheParty" to retrieve comma-separated list (requires Muler script) {& select get.?{Select...|Party,TheParty|Townies,TownNPCs|... => Utilize Roll20 query to choose the selected token set at run time (example requires Muler script) BONUS SYNTAX: !script --@forselected+|token-mod ... => forselected as downstream call, initiating downstream call (scriptcards calling forselected calling token-mod) Expanded forselected Options The forselected handle now accepts a custom escape character designation. The designated escape character is removed from the downstream call before it is sent to chat. Place the escape character in parentheses after the other options on the forselected handle: forselected(^) => use ^ as the escape character forselected-(\) => use \ as the escape character (the - is an auto-replacement setting, see previous post) forselected+-(=) => use = as the escape character (the + and - are auto-replacement settings, see previous post) With a command line like: !forselected(^) somescript --tgt|@^(selected.token_id) ...what is sent to chat by forselected will be: !somescript --tgt|@(selected.token_id) Which then resolves to the correct token id when it is received by the Fetch meta-script. (Note this won't work with the native Roll20 syntax  @{selected|token_id}  since that will try to resolve  before  SelectManager has the opportunity to restore the forgotten token to the message. You need the Fetch script to retrieve it, and you need to prioritize Fetch to run  after  SelectManager in the ZeroFrame loop. See the Meta Toolbox thread for more information.) Using select and inject With v1.0.0, you have the ability to select tokens virtually or to virtually add tokens to the selected set. Use either  {& select ...}  or  {& inject ...}  syntax structures, and include a comma-separated list of token IDs, token names, or approximate names of tokens to include. {& select -M1234567890abcdef, Haruk} {& inject Local Mookle, Biddy the Kill, Gorso} The difference between the two ( select  vs.  inject ) is that  select  replaces the selected tokens with those you designate, while  inject  adds the tokens to the selected token set (if they are unique). Using these tags can let you include a selected set of tokens even in a situation where you have used a targeting statement. The following statement would result in a message that did not have a selected array: !somescript --tgt|@{target|token_id} You can introduce selected tokens using either tag: !somescript --tgt|@{target|token_id} {& select Bargle} In fact, you can use a targeting statement as the source of the selected token: !somescript --tgt|@{target|token_id} {& select @{target|Use as selected...|token_id} Extend SelectManager Using Muler As mentioned in the Syntax Highlights, above, you can use Muler (another meta-scirpt) and a roll query to choose which token set to choose: !somescript arg1 arg2 {& select get.?{Tokens to select|The Party,theparty|Townies,townNPCs}}{& mule TokenSets} That would let you select which set of tokens you wanted to have replace the selected tokens of the message -- and it would happen at the time you run the message. This can save you from having to hunt up all of the party tokens on the board, or from having to deselect a mob of NPCs for a turn where you need the party selected, only to then have to reselect the mob.