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

InsertArgs: Substitution Script with Custom Replacement Hooks

1592504727

Edited 1595596114
timmaugh
Pro
API Scripter
Stop me before I go too far; I might be replicating existing functionality. Then again, as I'm new to R20 and maybe don't know all the ways people typically use it, tell me if there is more that could be done. :-) I wanted a script that could let me modify parameters for an ability on an ad hoc basis (see below for why I wanted this). So, the implementation of this would look like: {an ability} ==&gt; [has replaceable parts ("hooks")] {the script} ==&gt; [has functions that process values for those hooks] Straightforward. In practice, it would look like this... take an original ability like: OriginalAbility: &nbsp;&nbsp;&nbsp;&nbsp;!some_script --arg1:The Value --arg2:Another Value --arg3:So much value If that ability also accepted a "target" argument that would parse a list of token IDs separated either by space or by comma, you might at times need options like: &nbsp; &nbsp; !some_script --arg1:The Value --arg2:Another Value --arg3:So much value --target: @{target|Target 1|token_id} &nbsp; &nbsp; !some_script --arg1:The Value --arg2:Another Value --arg3:So much value --target: @{target|Target 1|token_id} @{target|Target 2|token_id} &nbsp; &nbsp; !some_script --arg1:The Value --arg2:Another Value --arg3:So much value --target: @{target|Target 1|token_id} @{target|Target 2|token_id} @{target|Target 3|token_id} &nbsp; &nbsp; ... Ugh. So, instead, drop a hook into that part of the Ability. In this case, I'll use "__target__" (what you use is up to you): &nbsp; &nbsp; !some_script --arg1:The Value --arg2:Another Value --arg3:So much value --target: __target__ And then write a new ability or macro with the InsertArgs script, using any/all of your hooks as arguments: &nbsp; &nbsp; !insertargs --load:OriginalAbility&nbsp;--__target__: [...] The InsertArgs script will take the original text of your Ability, replace the hook with the new value, and output it. In this case, since "@{target}" can't be processed from the API, the InsertArgs script "loads" the newly replaced text of the Ability into an Ability called "InsertArgs" (or creates it, if it doesn't exist), ready to be run. "But have we really accomplished anything?" I hear you say. "We've just carved out that part of the OriginalAbility command line to the new script... what have we really done?" Hold on, this is where it gets good... ...goodish... You could &nbsp;supply just raw text to the hooked argument to have it pass that straight through. But there are internal functions available to you to call to produce particular outputs. In this super-beta version of the script, there are two: gettargets( # ) targetsel() The&nbsp; targetsel() &nbsp;function doesn't take any arguments, but uses the tokens selected at the time the script is run.&nbsp;The gettargets() function takes a numeric value as an argument, and spits out that many @{target|Target #|token_id} statements to the hook. To make the gettargets() function really dynamic, feed it with an input query: !insertargs --load:OriginalAbility --__target__:gettargets( ?{Targets|1} ) (I would love to expand the list of available internal functions, but that's where my inexperience on R20 gets in the way. If there are ideas for other functions this script could do, let me know!) Code and Syntax API Call: Call it using either "!insertarg " or "!insertargs ". Arguments: Structure your arguments as " -- key :val" &nbsp;pairs. &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; key: your next hook &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp; val: how to replace that hook Special Arguments: The first argument tells the script how to operate. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; key: "chat" or other &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val: name of Ability to operate on The first argument's key is evaluated against "chat". If you want your finished output to go to the chat window immediately, use "chat". Anything else will tell the script to load the output into the InsertArgs ability. (Also, the internal functions know that you shouldn't send a @{target} to the chat, so they won't let you, and will instead load your output.) :-) Here is the code. All feedback is welcome, either of the "I tried it out..." sort or just as code review. EDIT: I dumped the code out to a Gist... <a href="https://gist.github.com/TimRohr22/0bd3a5f538fea0470b35ec871e479835" rel="nofollow">https://gist.github.com/TimRohr22/0bd3a5f538fea0470b35ec871e479835</a> EDIT : Newer version now in my Cauldron GitHub repo; still in pre-release beta: <a href="https://github.com/TimRohr22/Cauldron/tree/master/InsertArg" rel="nofollow">https://github.com/TimRohr22/Cauldron/tree/master/InsertArg</a> Version 0.2 Updates: -- the available functions are now independent objects. A library-style object is used to test user input against the available functions (keys), passing in the remaining arguments. Add a function, add a key to the library. -- multiple arguments can now be passed to the available functions by use of " :: " (space colon colon space) as the delimiter -- the gettargets() and targetsel() functions now accept a custom delimiter. For gettargets(), it is the second arg. For targetsel() it is the first. If it is not provided, a space is used. To get an empty delimiter, use the argument delimiter but don't supply a value.
1592511721
The Aaron
Roll20 Production Team
API Scripter
That's definitely interesting!&nbsp; I'll have to read it more thoroughly later. =D
1592517004

Edited 1592517120
timmaugh
Pro
API Scripter
That would be great! I was hoping to ask you about the line that is rem-ed out near the end of the handleInput function... I picked that up from one of your suggestions elsewhere, but I'm not sure what the |$1 is doing near the end. I know that the line as you suggested it would replace character specific references, which might or might not matter for this one... again, inexperience with the R20 objects coming through. I didn't want to implement it before I understood what that end bit was doing and could decide if it would matter. Other things I'm looking at... ...right now it sends as the player/character who sent the initial !insertargs call. Is there call for something else? ...right now it detects implementing a function just by the presence of parenthesis, which isn't helpful if you wanted to pass through a string containing a paren. So... probably a more robust parse or test of that part? ...the two functions I have right now have a space hard coded into the output of the chained IDs. Maybe let the user supply a delimeter in the arg structure: gettargets(3,",")
1592599040

Edited 1592599115
timmaugh
Pro
API Scripter
A Use Case I am adding a targeting option to my Hero Roller script. The output will use the images of the targeted tokens to report information specific to that token for the attack. It should look like this: I have 8 separate Abilities (as part of a character's martial art) that might need me to designate 1 (or more) targets every time I use them. And those targets could be @{target} calls or the set of @{selected}. I might also want to independently assign a game-specific "location" to the attack to drive damage multipliers. That's... a lot to maintain... or alter... or produce as API buttons.Instead I edit each ability to change those arguments to hooks. For example: !heroll --d:3.5d6 --t:k --pn:Way of the Circle --col:@{Heretic|color1} --n:area effect 4m, selective, -1 OCV per Target to all Targets --tgt:__tgt__ --loc:__loc__ And then I create ONE Ability that lets me pick between those 8 Abilities, feeding the substitutions as necessary: !insertarg --chat:?{Choose Power| Charity,WayOfCharity| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mountain,WayOfTheMountain| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Wind,WayOfTheWind| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;River,WayOfTheRiver| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Briar,WayOfTheBriar| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Leaf,WayOfTheLeaf| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Edge,WayOfTheEdge| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Circle,WayOfTheCircle} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --__tgt__:?{Choose Targeting Method| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Selected,targetsel()| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Targeted,gettargets(?{Targets&amp;#124;1&amp;#125;)} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --__loc__:?{Location| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Random| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Head| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Shoulder| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Arm| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hand| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Chest| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stomach| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Vitals| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thigh| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Leg| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Foot| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Focus| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Headshot| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Highshot| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bodyshot| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Lowshot| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Legshot} Now, if you're paying attention, you probably caught that the location parameter could have been accomplished as a query in each of the specified 8 Abilities... but that would then be 8 places I would have to touch to update should the query change. Doing it this way gives me 1 location to make a change that affects ALL the Abilities called. I have achieved... ONE ABILITY TO RULE THEM ALL. I shall diminish. And go into the west.