
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"