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

[Meta Script] Muler - get/set variables, static tables

1618936992

Edited 1628091141
timmaugh
Pro
API Scripter
Muler FILE LOCATION:  In the 1-click install (also in  My Personal Repo ) 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:  Muler provides a way to store values on a character sheet in an ability (called a Mule). Syntax tokens can be used in any API command line to load a mule, retrieve a variable, and/or set a variable. Since the variable is stored on a character sheet, it persists between rolls, game sessions, or even campaigns. Introduction Mules are abilities on a character sheet that can help you track information across rolls, game sessions, or even, since they are stored on the sheet, campaigns. This could be an inventory, a series of condition mods, roll history, or even tables from a game system. You can have as many mules as you want on any character, and you can access any mule on any character you control. Syntax Highlights Drop these syntax tokens into any API command to trigger the associated behavior. They will be filtered out (or replaced with the appropriate data) by the script. {& mule ModTable}                                => loads the first ModTable mule it finds (from characters you control) {& mule TableMule.ModTable}                      => loads the ModTable mule from the TableMule character {& mule Cake Izzard.OrDeath}                     => loads the Cake mule (first found) as well as Izzard's OrDeath mule get.4                                            => if mules are loaded, attempts to retrieve the variable "4" get.ModTable.4                                   => gets the variable "4" from the ModTable mule (ModTable must be loaded) get.TableMule.ModTable.4                         => same, but specifically from the TableMule character set.Izzard.Cake.Rush = We're all outta cake/set  => sets the "Rush" variable in Izzard's Cake mule to "We're all outta cake"                                                     note the ending /set to close the variable's value Mule Creation / Formatting You can create a mule ability yourself, though if APILogic detects that the mule you have created doesn’t exist, it will try to create it. Mules are formatted as lines of  variable=value  : initMod=4 FavTeamMember=Mo the Raging LeastFavoriteTeamMember=Lizzie PurePants Any lines that do not follow this format are not included in the set of parsed variables, so you if you wanted to add add in headers or grouping, you can: === MODS === initMod=4 === TEAM DYNAMICS === FavTeamMember=Mo the Raging LeastFavoriteTeamMember=Lizzie PurePants But be aware that if a variable does not exist, APILogic will create it, and it will create it at the bottom of the list. You can move it later without causing a problem, or you can leave it there. In fact, maybe you want an  === UNCATEGORIZED ===  section. It’s up to you. Mule and Variable Naming Obviously, since a Mule is a character sheet ability, it cannot contain a space in the name. Variable names also may not contain spaces or an equals, though the value of the variable is free to be whatever you can fit on one line. As demonstrated in the  Syntax Highlight , above, Muler uses dot-notation to refer more specifically to variables, if necessary (i.e., character.mule.variable); you can continue this notation  within  your mule if you wanted to store similarly-named variables in the same mule but differentiate them from each other. For instance, if you wanted a table of EncumbranceMods (with the Encumbrance a character is carrying related as 0, 1, 2, etc.) in the same Mule as you wanted to store a table of FatigueMods (also in 0, 1, 2, etc.), you might use dot-notation in the names: EncMods.0=0 EncMods.1=0 EncMods.2=-1 EncMods3=-1.5 ...etc. FtgMods.0=1 FtgMods.1=1 FtgMods.2=1.1 ...etc. Though it might make more sense to store this information in separate Mules. Loading a Mule Use the  {& mule ... }  tag to load Mules and make their variables available for you in your command line. You can separate multiple mules to load with spaces within the same tag. Mule retrieval happens  before  variable retrieval, so it doesn’t matter where in your line you put your Mule statement. !somescript --stuff|get.thestuff {& mule ModMule} --tacos Muler uses a “least common reference” (LCR) to identify the Mules to load… meaning that if you control two characters who each have a Mule named “ModMule”, both will be loaded by the above statement (see below how to reference their variables independently). To load a “ModMule” Mule from only one character, use the dot notation: {& mule Viper.ModMule} Getting a Variable Once you have one or more Mules loaded, you can use a  get.varname  to retrieve it in your command: !somescript --mod| get.initMod {& mule Viper.ModMule} The get statement can take these forms: get.varname get.mulename.varname get.character.mulename.varname When a variable is loaded from a Mule, it takes over the location for that variable name, that mule.variable name, and that character.mule.variable name. That means that the LCR (get.varname) will always be filled with the  last variable  of that name to be found, and the get.mulename.varname version will always be filled with that variable from the  last mule  of that name to be found. EXAMPLE: Naming Precedence You load the Mule “MyLittleMule” using the statement: {& mule MyLittleMule} However, you have two characters, Viper and Jester, who each have a MyLittleMule attribute. Viper’s looks like this: initMod=4 EncMod=5 motto=There's no time to think! bestrecent=20 Jester’s looks like this: initMod=3 EncMod=3 Motto=Where'd who go? If Viper loads before Jester, the map of variable reference would look like this: VARIABLE REFERENCE                 |  CHAR  | VALUE -----------------------------------|--------|----------------------------- get.initMod                        | Jester | 3 get.EncMod                         | Jester | 3 get.motto                          | Viper  | There's no time to think! get.Motto                          | Jester | Where'd who go? get.bestrecent                     | Viper  | 20 get.MyLittleMule.initMod           | Jester | 3 get.MyLittleMule.EncMod            | Jester | 3 get.MyLittleMule.motto             | Viper  | There's no time to think! get.MyLittleMule.Motto             | Jester | Where'd who go? get.MyLittleMule.bestrecent        | Viper  | 20 get.Jester.MyLittleMule.initMod    | Jester | 3 get.Jester.MyLittleMule.EncMod     | Jester | 3 get.Jester.MyLittleMule.Motto      | Jester | Where'd who go? get.Viper.MyLittleMule.initMod     | Viper  | 4 get.Viper.MyLittleMule.EncMod      | Viper  | 5 get.Viper.MyLittleMule.motto       | Viper  | There's no time to think! get.Viper.MyLittleMule.bestrecent  | Viper  | 20 Note that the variables are case-sensitive (“motto” vs “Motto”), and that it is only when you arrive at a unique piece of identifying data (in this case, the character name) that you are able to differentiate between the similarly named (and/or similarly-muled) variables. The point is, you should use the LCR guaranteed to get you the variable you intend to retrieve, but if you have only one Mule, you can use the simplest form for every variable. Setting a Variable’s Value Set a variable’s value using the text formation  set.varname = value /set . The implication of the LCR during setting is that all less-specific references to the variable are set, across all Mules, and among the available ways of referencing that variable. In the above example (Jester and Viper having similarly named Mules), the following statement: set.initMod = 8/set …will set the initMod variable in both Mules to be the same value, 8. In fact, using the Mule name in the set statement would still result in both Mules being updated, since the name is shared between them. The only way to set only one of the variables would be to use the LCR, and qualify the name with the character’s name: set.Viper.MyLittleMule.initMod = 8/set In setting that value, the less specific ways of referring to the variable (get.initMod and get.MyLittleMule.initMod) are also set, so that further references to these during this cycle of evaluating the command line will retrieve the new value (for instance, in the ZeroFrame loop). Mules as Static Access Tables Rollable tables on Roll20 do a lot to provide random results from weighted entries, which can be good for things like random encounters or the like, but which aren’t as helpful for times when you  know  the value from which you need to derive the result. For instance, if a given level of a character’s Stamina has a direct correlation to a mod applied to their activities, you don’t need a randomized result… you need the result directly tied to what the character’s Stamina is when you consult the table. Similarly, some systems have charts built for how rolls map an attack roll to damage -- for instance, crit tables. A Mule can fill this gap. Construct your Mule as the entries of the table, with the various states of the referenced input as the variable names. For an Encumbrance Mod table that would return a modifier to rolls based on the weight of the items the character was carrying, that might look like: 0=0 1=0 2=-1 3=-1 4=-1 5=-2 ...etc... If the Mule were named “EncumbranceTable”, you can reference that using the character’s CarryWeight attribute like so: ... {& mule EncumbranceTable} ... get.@{selected|CarryWeight} ... Note that formation uses the Roll20 parser to retrieve the CarryWeight for the character. You could do something similar with a Fetch construction. Muled tables also support ranges of numbers as the variable name, so that if you had a Mule like this: <=4=Value for less than or equal to 4 5-7=Value for 5 to 7 8=Value for 8 9-12=Value for 9 to 12 >=13=Value for 13 or more You can ask for a variable by numeric value to find its place on the table: get.10    => returns "Value for 9 to 12"
1618937005

Edited 1626814944
timmaugh
Pro
API Scripter
Change Log v 1.0.0 - Initial Release v 1.0.1 - improved regex search, provided end to get statement constructions v 1.0.2 - added native handling of line breaks v 1.0.3 - added ability to wrap syntax constructs in parentheses to avoid text parsing errors v 1.0.5 - fixed bug where entire get statement is left in place if Fetch cannot resolve the property/attribute v 1.0.7 - fixed ordering bug that could potentially let Muler run too soon
1618937012
timmaugh
Pro
API Scripter
Advanced Usage and Tricks
1619624908
timmaugh
Pro
API Scripter
Version 1.0.1 Released April 28, 2021 FIXED : Minor typo in error reporting. CHANGED : Better regex construction for detecting variables and Mules; spaces in character names is no longer supported (use approximate names or character IDs, if necessary) ADDED : Syntax token to designate the end of a get statement in case statements need to be butted up against bordering text (see the rest of this post) Update Notes A Way to Terminate Get Statements You can now use the formation /get to designate the end of a get statement. This mirrors the /set formation that denotes the end of a set statement, however, contrary to set , this closing tag is not required for get . get.TheVariable/get get.Mule.TheVariable/get get.Character.Mule.TheVariable/get In cases where there is a space (or a period-space combination) following a get statement, you wouldn't need to use the /get  construction. However, if you have to place two retrieved variables back to back in the command line, or if there is other text that borders the get statement, then you should use the /get as a way to denote the end of the name of the variable you'd like to retrieve: !somescript --value|get.TensDigit/getget.OnesDigit In the above, the /get breaks up the two variables and lets the parser detect where one ends and the next begins. Character/Token Names with Spaces Supporting character names with spaces led to unforseen interactions in the command line around detecting where a get statement started and ended. Due to this, support for spaces in a character's name had to be removed (for now -- I will continue to look for a way to include this functionality in a future release). You must use a close-approximation of the character's name that does not include spaces, or the token or character id.
1619628526
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
I think there are some great possibilities here! I need to come up with a project that requires it so I can make myself sit down and learn it.
1620076342

Edited 1620096347
timmaugh
Pro
API Scripter
Version 1.0.2 Released May 3, 2021 ADDED : Native handling of line-breaks Update Notes Since this meta-script can be used with or without the ZeroFrame Meta Toolbox, it needs to be able to handle line-breaks natively. This update fixes a problem where this script would not have processed correctly were it used in a multi-line macro without ZeroFrame installed, too.
Can I ask for an explanation?
1620091845

Edited 1620096329
timmaugh
Pro
API Scripter
Of the update? Certainly... First, you would get the most functionality out of this script by having ZeroFrame installed, as well. If you did, then ZeroFrame would loop over the set of installed meta-scripts until all of the meta-changes were finished. As a part of that looping, ZeroFrame would be in charge of handling line breaks, and Muler would not have to. I realize, however, that not everyone is going to install ZeroFrame, and that they might only want the ability to set and retrieve variables. In that case, Muler has to be able to handle the line-breaks on its own. This is because Muler is going to handle (and change) the message before it ever reaches the intended-recipient script, and it needs to be able to hand of the message in the same condition it found it except for the things that you wanted Muler to change. If you enter: !somescript --arg1|get.MyVar {& mule MyMule} ...then Muler needs to be able to return the value of the MyVar variable from the MyMule mule to the appropriate position of the command line before it hands the message off to the script that will catch the "somescript" handle. If the value of that variable were "tacos" ,  Muler would have to hand off: !somescript --arg1|tacos If the original command line text included line-breaks, Muler needs to be able to handle those, too: !somescript {{ --arg1|get.MyVar {& mule MyMule} }} ...and give back a message like the original... !somescript {{ --arg1|tacos }} EXAMPLE : Imagine you have a series of token-mod macros that put a "status" into a token's bar3 to reflect a delayed-onset effect of some condition. One of those macros might be constructed like this: !token-mod {{ --set bar3|"Woozy" }} If you wanted to replace "Woozy" with a variable, you could set up a Mule (ability) called "TrackingMule", and use it to store the delayed effect "DelayedCondition". When the character takes an action that would trigger the delayed onset of the condition, you would store the condition to be applied in the variable (that's beyond the scope of just explaining the line-breaks, so just imagine that as a part of some other script/action you stashed the appropriate data in the variable). Now the condition is just waiting for you to apply, and you can retrieve it and apply it like this: !token-mod {{ --set bar3|"get.DelayedCondition/get"{& mule TrackingMule} }} That loads the Mule (making the variables available) and retrieves the one you need. You have reduced your series of token-mod macros down to 1 and avoided a roll query asking you which condition to apply. Because the above macro is multi-line and you don't have ZeroFrame there to handle the line-breaks, Muler needs to be able to handle the line-breaks itself so that it properly detects all of the work it needs to do in the script and can hand off the finished message "as it found it" (minus the changes it performed).
1620420181
timmaugh
Pro
API Scripter
Version 1.0.3 Released May 7, 2021 FIXED/ADDED : Tag buffering with parentheses of brace-enclosed tags, i.e.,  ({& mule... }) Update Notes This fix gives an option if the command line might result in a series of brace characters that would be inadvertently eaten by Roll20 parsers. In that case, you can enclose the syntax token in parentheses to break up that series of characters. This only matters for brace-enclosed tags: {& mule CritTable} or ({& mule CritTable}) This has no effect on .get or .set statements.
1620743653
timmaugh
Pro
API Scripter
Muler has been submitted to the one-click, and should be available from there with the next merge.
1620830490

Edited 1628091767
timmaugh
Pro
API Scripter
Version 1.0.5 Released May 11, 2021 FIXED : Bug that did not correctly handle .get statements in some cases Update Notes If Muler doesn't find the variable, it leaves the .get construct in place so it can get it in another pass of the ZeroFrame loop (if it has the opportunity and the variable has been populated by then). In previous versions, if the construct had been bounded by a /get on the backend, that portion of the construct was lost, leading to potential interference from nearby characters in the command line. This update ensures that the entire .get construction is preserved, preventing this problem. New Muler Thread This thread has become locked due to inactivity. You can find the new thread here .