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] InsertArg -- script preamp, siege engine, menu maker, command line constructor

1616619225
timmaugh
Pro
API Scripter
Hey, Erik... what do you mean when you say: The train goes off the rails though when I change the ScriptCard macro to include an attribute call like   --=Roll|1d20 + @{Character|stat_modifier} What happens, then? It's been a while since I was elbow deep in that part of the code, but IA *should* just be reading the text of the ability and copying it across, with the appropriate replacements... nothing in that process should be invoking the Roll20 parsers to try to resolve that attribute call. Which... is what you would want, right? You don't want the attribute call to resolve until you run it from the destination character... so that you get the right information. If that is the case, then I don't think the rslv parameter will help you. That's so you that you can find syntax constructions like @{character|attribute} and resolve them without having to type that fully into your IA command line. But that whole process would result in an early resolution of the attribute call, which isn't what you want. One other thing... if we can't get this to work just with IA, I am about to release another metascript that allows retrieval of token or sheet data *after* the Roll20 parsers but *before* other scripts get the message, while simultaneously letting you define a "default" value if the thing isn't found. I call it the metascript "Fetch", and it is a part of the larger MetaToolbox release I'm putting together. If we could use that, you could replace that @{character|attribute} call with something like @(character.attribute) or @(character.attribute[default]). That wouldn't get picked up by the Roll20 parsers, and wouldn't be seen by the API until the ability was run from the destination character. Until then, though... what happens when you use the attribute call in IA?
You are exactly right.  I want the @{Character|stat_modifier} to be replaced with the character name  #getme{{!!r#n}}  and stat selection  ?{Attribute|Strength|Dexterity|Constitution|Intelligence|Wisdom|Charisma}  from the dropdown so that by the time ScriptCards sees it, it would be something like @{Bob|Strength_modifier}.  Running it with the call in the macro I get a button l (From API):   Loaded Ability When that is clicked, I get in the chat No ability was found for %{Banshee|InsertArg} Banshee: Banshee|InsertArg I also notice, that in testing, if I set myself to speaking as a PC, it whispers only to the PC and not to me (as GM) at all. But it does whisper them the same default looking button.  And just in case it matters I have installed "ŦŦ InsertArg v1.5.2, 2021/2/6 ŦŦ -- offset 25218" "ŦŦ XRAY v1.2, 2020/9/17 ŦŦ"
1616645789
timmaugh
Pro
API Scripter
To defer the selection of the attribute until it is run from the character, I think we have a couple of options... You can try encoding the braces of the query with their HTML counterparts: --stat#?{Attribute|Strength|Dexterity|Constitution|Intelligence|Wisdom|Charisma} ...to see if they survive. If that doesn't work, you can try double-encoding them: --stat#?{Attribute|Strength|Dexterity|Constitution|Intelligence|Wisdom|Charisma} However, the simplest thing (simple being relative as InsertArg is concerned) might be to look up the 'query' internal function for IA. You should be able to pass it a prompt  argument for the query, and list argument of the values. From the help: query type: function Usage Turns a list of options (l) into a query using prompt argument (p). Alternatively, produces a nested query by including the argument "n". For internal calls, supports an array object passed to "a" instead of a list. ARGUMENTS p prompt for query (or nested query) output; default: Select l list of options to use as query rows; can be pipe-separated labels, or a series of pipe-seperated (label),(value) options n whether to nest the query, replacing the three characters: } , | with their html entities only where they are not involved in an attribute or ability lookup So that would be something like: --stat#query{{!!p#Attribute !!l#Strength|Dexterity|Constitution|Intelligence|Wisdom|Charisma}} I apologize that I'm a bit rusty with putting this together, but just so we're on the same page... given the updated line, below: !ia --chat#GenericMacro|AbilityCheck --Character#getme{{!!r#n}} --stat#query{{!!p#Attribute !!l#Strength|Dexterity|Constitution|Intelligence|Wisdom|Charisma}} What you and IA are doing is reading the contents of the AbilityCheck ability on the GenericMacro character. After you perform your hook replacements, you are going to output it to chat. You use the getme{{}} function to get the name of the speaker (which you sub in for the hook 'Character'), and you want to sub in the query string for the "stat" hook. Since you didn't supply a "store" parameter, IA should load the resulting command line into an ability called "InsertArg", which is what the button is pointing at. If it is telling you that such an ability isn't found on the character, check the GenericMacro character. IA likely dropped the resulting command line in an ability there named "InsertArg". If that is the case, you probably have to provide the "store" parameter for the destination field on the target character (the character you're speaking as), as discussed in this post . Hope this helps!
1616651812

Edited 1616653591
Victor B.
Pro
Sheet Author
API Scripter
I've got to be honest here.  Great menu.  I've read through this multiple times.  What does this do? I'm still not clear what problem this solves. 
1616676538
timmaugh
Pro
API Scripter
What does InsertArg do? Why, man, it slices, it dices... it cares for your pet chinchilla and never forgets to check in on your mother. In short, it's a way of gathering sheet info, formatting it, and outputting it in such a way that you can construct command lines on the fly. I'd aimed for it to fill a niche as a query language. Once you have that (the ability to retrieve and format the information) menu construction was a natural outgrowth.
Hey Tim,  The query worked well, but the same behavior is still happening. I pulled back and tried something simple. I created an ability on the GenericMacro character called Test2 with the line: /w GM @{Character|level} Then ran the command: !ia --chat#GenericMacro|Test2 --Character#getme{{!!r#n}} The same behavior happens here. The Loaded Ability button with the same No Ability found error. I checked the sheet and no ability with InsertArg was created. 
1616702699

Edited 1616702807
Ok I am getting closer as I hit this thing like a monkey opening a coconut.  So I thought, hey it looks like things go down when we use the @ symbol, so what if I replace that with the HTML code for it.  Here is the updated line in the card:    --=Roll|1d20 + @{Character|stat_modifier} And here is the output:  As you can see in the tool tip, the hook replacements are being done flawlessly, but now the attribute isn't being retrieved from the sheet. It's just being output as text. I feel like I am doing something wrong here. 
1616729795

Edited 1616729915
timmaugh
Pro
API Scripter
I think I see where our misunderstanding is... sort of. IA can deliver a formatted command line to the chat, button, or loading into a given ability. This can be a way to formalize/standardize abilities for all of your players, or to copy an ability from one character to another. Separately, you have an issue where the formation like @{character|?{Attribute|Strength|Dexterity|Constitution}} ...won't work. However, to construct the call with IA (alone) won't work because by the time IA has received the message the Roll20 parsers have already had their crack at the value. That's why it shows up in your roll-tip "unretrieved" and still in the @{Dark Naga|Strength_modifier} format. We have a couple of options to make it work -- and they can be used in conjunction with each other. The question is what fits the goal you're looking to achieve. Original Problem: simplify many attribute checks into one ScriptCard-calling ability Since you can't do the above form (@{Character|?{Query}}) in one step. You have to have a way to defer the attribute call until the query is finished. IA doesn't provide this for you... but APILogic does. By invoking a syntax token that APIL detects (and removes), but which doesn't alter the command line, you trigger APIL to cycle the message until it doesn't see one of its syntax tokens. By cycling, it invokes the Roll20 parsers again. With APILogic installed, this *should* work: !scriptcard {{ {&if a=b} {&end}   --#whisper|GM   --#title|Character ?{Attribute|Strength|Dexterity|Constitution|Intelligence|Wisdom|Charisma} check   --+Rolling|Character rolls a ?{Attribute} check.   --=Roll|1d20 + @\{Character|?{Attribute}_modifier}   --+Result|Character has rolled a [$Roll] for their ?{Attribute} check.    --? [$Roll] -ge 10|Success   --+Failed|Character has failed their ?{Attribute} check.    --X|   --:Success|   --+Success|Character has succeeded their ?{Attribute} check.  }} On the first pass, the Roll20 parsers will detect the query and prompt you to resolve it. Then APIL steps in front of ScriptCards and detects the {&if} construct (which evaluates as negative and nothing is included). Then APIL unescapes the command line (removing the backslash), and invokes the Roll20 parsers again. This time, the @{Character|Strength_modifier} attribute call is caught and resolved. APIL is done, and it releases the finished command line for ScriptCards to pick it up. You see the problem that I left "Character" in the text. Obviously you have no characters actually named this, so that's where we lean on IA. (read on...) Getting the macro from here to there You have 15 characters, and they all need this ability, and it needs to be customized for them (not reading "Character" but "Dark Naga"). That's where IA can help. Put the above command line in an ability on the GenericMacro character. We'll call the ability "GenericSCAttrCheck". Next, you would need to speak as Dark Naga and use the following command: !ia --load#GenericMacro|GenericSCAttrCheck --store#SCAttrCheck --Character#getme{{!!r#n}} Dark Naga should now have an ability called "SCAttrCheck" that has the same text as the original, except that the "Character" hook has been replaced with "Dark Naga". Change the "speaking as" character to the next you need to update, hit the up arrow, and hit enter. Keep going until you've spoken as all of the characters that need updating. (If you have 10 such macros that you need to update and transfer from GenericMacro to your characters (properly personalized), creating 10 abilities on your 15 characters, you could drop 9 other similar lines into a single macro -- an actual macro, not an ability -- and run it as each of the 15 characters to get all 10 in one go for that character.) Note that this process decentralizes the command line text to each character, but it makes running the ability a one-step process for the player. It is one-step in that once the ability is created, the player only has to click the appropriate button to have the outcome hit the chat interface. However it is decentralized in that the command line now resides on the destination character's sheet (ie, Dark Naga's sheet). If you needed to tweak the command line in the future and you wanted to redeliver it to all 15 characters again, you'd have to go through the same process of running your IA command to "load" the ability from GenericMacro into your "speaking as" character. The other alternative is... Alternative: centralized command line with 2-step process Given the same "GenericSCAttrCheck" macro on the GenericMacro character, the following will work: !ia --button#GenericMacro|GenericSCAttrCheck --Character#getme{{!!r#n}} Running that from Dark Naga's sheet (or just cut & pasted when speaking as Dark Naga), you will get the proper command line stored in an ability called "InsertArg" on Dark Naga's sheet, and you will get a chat button to run that InsertArg ability (it will say "Loaded Ability"). Dark Naga's player would then click on the chat button to run the ScriptCard call. This keeps the language of the macro centralized in case you need to update it at any time, but it does make for a 2-step process in that your players have to click their ability to run the IA command line, then the chat button to run the ScriptCards command line. This isn't ideal when APILogic is already doing the heavy lifting to make a roll query work from within an attribute call. In that case, I would suggest a hybrid alternative... Hybrid Alternative If on GenericMacro you created not only the various abilities your players need (like "GenericSCAttrCheck"), but also an "UpdateGenerics" ability, then you could give your players the "UpdateGenerics" ability, and let them know when they need to run it to get updated command line syntax for the others. As an example, imagine you had 10 abilities like "GenericSCAttrCheck" you needed to get to your 15 characters. First, you create each in a similar fashion to what we did with "GenericSCAttrCheck", above. Then you create one more, called "UpdateGenerics" that has ten entries for IA: !ia --load#GenericMacro|GenericSCAttrCheck --store#SCAttrCheck --Character#getme{{!!r#n}} !ia --load#GenericMacro|GenericSomething --store#Something --Character#getme{{!!r#n}} --NiggelNugget#Spectral Wham ...etc... Finally, go through the process of speaking-as each character 1 time to copy over this "UpdateGenerics" to each: !ia --load#GenericMacro|UpdateGenerics --store#UpdateGenerics Now each character should have an ability which, when run, will copy over the 10 abilities from GenericMacro -- creating them if they don't exist or updating them if they do. Clicking on any of those 10 delivered abilities will be a 1-step process for the player, but at any time you can ask them to run their "UpdateGenerics" to bring their local versions of the abilities up to the latest version you have at GenericMacro. Future Solution One of the metascripts I'm about to replace can, like I said, retrieve character, token, or speaker data. By locating the speaker's name, and by deferring the resolution of the attribute call until both it and the roll query have completed, you wouldn't have to copy anything to the characters' sheets at all. They could run it directly from GenericMacro, and all of the pertinent/particular information would populate properly. That's a lot of p's in that sentence. If you can't get part of this to work, shoot me an invite and promote me. We'll get it sorted.
1616738958
Victor B.
Pro
Sheet Author
API Scripter
And you think a non-technical pro user is going to get this?  I've been in tech for 30 years and I still don't get it.  You need better marketing .  
Victor B. said: And you think a non-technical pro user is going to get this?  I've been in tech for 30 years and I still don't get it.  You need better marketing .   ;)
Tim, you are a brilliant genius! I got some time today to read over your advice and you are totally right. I forgot about the order of Roll20 operations. So then I thought, well if attribute calls have to be done first, then why not pull those calls out of the card and into IA! I built the card using roll and string variables assigned to replacement hooks.  !scriptcard {{    --#whisper|GM   --&TokenName| Character   --#sourceToken| CharID   --&ScoreName| AttributeName   --=RollMod|[*S:[&ScoreName]_modifier]   --#title| Character [&ScoreName] check   --+Rolling| Character rolls a [&ScoreName] check.   --=Roll|1d20 [Base] + [$RollMod] [Modifier]   --+Result| Character has rolled a [$Roll] for their [&ScoreName] check.    --? [$Roll] -ge 10|Success   --+Failed| Character has failed their [&ScoreName] check.    --X|   --:Success|   --+Success| Character has succeeded their [&ScoreName] check.  }} Then I used IA to replace those variables with the actual calls in the IA command line.  !ia --chat#GenericMacro|AbilityCheck --AttributeName#?{Attribute|Strength|Dexterity|Constitution|Intelligence|Wisdom|Charisma} --Character#@{selected|character_name} --CharID#@{selected|token_id} This way instead of 6 separate Attribute Macros on 7 different PCs, each person uses the same 2 commands for everything I need.  As always, thanks for all your help!!
1616931497

Edited 1616932117
David M.
Pro
API Scripter
EDIT - it appears that I was responding to your first post from several days ago, lol. You got it working, so the following is extraneous, but I'll leave it here because why not. I was a little brute force with the attribute assignment - you could probably use [*S:...] syntax with substitutions but oh well :) Erik, FWIW, it seems like you can do what you want all from your scriptcard. The following works for 5e attributes, for example. The trick is to match the name of some attribute variables to the result of your attribute query, as well as assigning the query result to a string variable. Then use a nested variable syntax in your --=Roll assignment.  !scriptcard {{ --:VARIABLE ASSIGNMENT| --&Attribute|?{Attribute|Strength|Dexterity|Constitution|Intelligence|Wisdom|Charisma} --=Strength|@{selected|strength_mod} --=Dexterity|@{selected|dexterity_mod} --=Constitution|@{selected|constitution_mod} --=Intelligence|@{selected|intelligence_mod} --=Wisdom|@{selected|wisdom_mod} --=Charisma|@{selected|charisma_mod} --=Roll|1d20[BASE] + [$[&Attribute]][MOD] --:FORMATTING PROPERTIES| --#whisper|GM --#title|@{selected|character_name} [&Attribute] check --:OUTPUT| --+Rolling|Character rolls a [&Attribute] check --+Result|Character has rolled a [$Roll] for their [&Attribute] check. --? [$Roll] -ge 10|Success --+Failed|Character has failed their [&Attribute] check. --X| --:Success| --+Success|Character has succeeded their [&Attribute] check. ​ }
Hey Tim, back again! I am durdling with something and was trying to see if IA can make it easier or if one of the other metascripts is what I need So I am building a table of wild magic effects. Can IA replace hooks in the actual roll of the table? Like if I have an entry that reads "Character dances like a puppy", can I somehow pass in the name of the character itself if that entry is rolled to then output "Heretic dances like a puppy"?
1620356065
timmaugh
Pro
API Scripter
Yep, with the Meta Toolbox released, I think that is just what you're looking for. You might have to provide more details, but the basic idea would be to have ZeroFrame, Fetch, SelectManager... possibly Muler installed. The flow would go (if I understand your description)... Send chat command (this might oversimplify it if it's a ScriptCard call, but roll with me) Roll20 handles all of your inline rolls, queries, and sheet/token requests (including rolling against the rollable table) This would leave a roll marker in the command line (like, $[[0]]), and would typically be the end... ... but you can do jujitsu . You slap a .value on the end of your table roll, and ZeroFrame will unpack the result: [[1t[ShiaLaBeouf] ]].value How does that help? For a rollable the .value   extracts the table text for your returned item. If, in that text, is another meta construction, then when the ZeroFrame loop comes around again, the meta construction will be caught and new inline rolls will be rolled. Here are some options: Options using a Fetch construction... If the character is the speaker... Out of the corner of your eye, you spot him. @(speaker.character_name). If the token is selected... He gets down on all fours and breaks into a sprint. He's gaining on you! @(selected.character_name). Options using Fetch and SelectManager With this construction, the token doesn't need to be selected; it will be virtually selected (as far as the message is concerned). Put this somewhere in your main macro, if you know who is going to be doing the calling of *this* particular call: {& select ShiaLaBeouf } Or, allow yourself to choose among the party: {& select ?{Who is the actual cannibal?|Shia,ShiaLabeouf|Hollywood Superstar,ShiaLaBeouf} } Or allow yourself to target a token: {& select @{target|Actual Cannibal|token_id} } Any of those establishes the selected token for the message, so then your table line item can access that information: Running for your life from @(selected.character_name). Options using Muler, possibly Fetch This is a little bit silly for a retrievable item like a character name, but I'll include it for completeness in case there is another data you want to collect. If individual characters had mules that ultimately had a variable of the same name, you could differentiate by loading the mule in the main macro. Again if you know the character: {& mule ShiaLaBeouf.Cottage} ...if you know the token will be selected, and the mules will all be named "Cottage": {& mule @{selected|character_name}.Cottage} ...or the mule to retrieve is in the token's bar 3 current: {& mule @{selected|bar3_value} } ...or the mule might be in an attribute called "DefaultMule", but you want to fail to a fallback mule/table if that attribute isn't defined (using Fetch): {& mule @(selected.DefaultMule[FailMule]) } (The above might require an ordering statement to make sure Fetch runs before Muler): {&0 fetch get} Then your table return can have a .get statement to retrieve the variable you want from that mule: Legendary fight, with get.ActualCannibal/get. Normal Tuesday night for get.HollywoodSuperstar/get Options using any of the above with InsertArg Remember that the Meta Toolbox will handle the message before InsertArg gets it, so once ZeroFrame has unpacked the inline roll against your table (and whether or not other meta scripts have acted on the syntax structures embedded in the returned text), if an InsertArg hook makes it into the command line that IA is working on, it will substitute into that hook any of the data you tell it to get. You could have a rollable table of random responses from an NPC (Shia LaBeouf, obv), each of which present a different chat menu of 5 options for you to choose from as GM. Those line items for the IA menu could be housed in the table, brought to the command line by ZeroFrame, and then turned into a menu by IA. Anyway, lots of options there. If one of those doesn't seem to get you where you want to be, post back and I'll help you figure it out.
Just a normal Tuesday night for that guy.  I will start to really play with this over the weekend and see what I come up with. Thanks!
1620401038
timmaugh
Pro
API Scripter
There's a meta construction on his face. My GOD! There are meta constructions everywhere! =D
1623777892
timmaugh
Pro
API Scripter
Version 1.6.0 Released June 15, 2021 (headed to 1-click next week, or get it from my repo today) Comprised Of: Core Engine : 1.5.3 Core Lib : 1.5.2 XRay : 1.2.1 ADDED:  Multi-line support for both InsertArg and XRay calls ADDED: Functions may now use (( )) instead of {{ }} ; required for multi-line commands CHANGED: Version number is now independent of 3 components, and will increment with any change FIXED: A problem with the getrepeating{{}} function was preventing returns FIXED: XRay now accounts for ghost entries and removes those duplicates Update Notes Multi-line Support and Double Parentheses You can now use the {{ }} construction (as seen in other scripts) to break your arguments to multiple lines. If you use a function within a multi-line command line, replace the bounding {{ and }} with (( and )), respectively. This will keep the Roll20 parser from treating the end of your function as the end of the multi-line input. (The double-parens may be used in a single-line command, as well; they are simply required if you are sending a multi-line command bounded by {{ and }}.) This allows line breaks before the double hyphen denotation for an argument, and before the double-exclamation mark denotation of a function parameter. Note, however, that any argument that includes a function must include the opening (( of the function. For instance, the getrepeating(()) function of the following command: !ia {{   --whisper   --show#getrepeating(( !!c#Heretic     !!s#skills     !!sfxa#skill_roll_target     !!d#`br` )) }} The --show line must proceed to the (( of the getrepeating function. The )) end to a function can come on the same line as the last argument, or on its own line (as above). The XRay command line takes no functions. The double brace to end the multi-line input can come on the same line as the last argument, or on its own line: !xray {{   ---M4jpPEy0ScLWE54K2gM#moves   --?{At position...|0} }} Ghost Repeating Attributes A Roll20 bug in character sheets sometimes creates "ghost" entries of elements in a repeating list. They don't manifest to the character sheet, nor do they appear to interfere with sheet retrieval (i.e., @{Character|repeating_list_$0_attr} ), however they would output individually for XRay. That meant that for a list that contained three items (A, B, and C), XRay might show you 8 versions of A, 3 versions of B, and 5 versions of C, and their "row number" position on the character sheet would be incorrect. Now XRay filters these ghost entries out and only shows one entry for each, where the row number of each should be accurate to their position on the character sheet: repeating_list_$0_attr