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 .
×

This Week in Metascript Development - Fetch Gets All Kinds of Updates

1779216910
timmaugh
Pro
API Scripter
Fetch is a metascript (and part of the Metascript Toolbox) that allows real-time access to virtually any game data. You can use this access to return data to your command line -- either command lines that are intended to continue on to standard scripts or those intended for chat panel output. Since its initial development and release, the Fetch metascript has required me to periodically update it to stay abreast of new properties Roll20 added to game objects, or to whole new objects Roll20 added to the game experience (i.e., when they added pins). With this update, not only does Fetch get a bunch of new properties and methods of access, but it also gets a new detection engine that will automatically poll new objects and new properties to expose them for your in-game use. Before continuing, if you don't know the difference between a metascript and a standard script, I suggest you watch at least the first 15 minutes of this video ( <a href="https://www.youtube.com/watch?v=unBCYjRnOQg" rel="nofollow">https://www.youtube.com/watch?v=unBCYjRnOQg</a> ). As mentioned, Fetch is part of the Metascript Toolbox, which is just a set of metascripts designed to work together to enhance what you can do on the Roll20 platform. If or when you are interested enough to get Fetch to give it a try, I would suggest getting the whole Toolbox to get the full functional experience. Now, on to the update... The Great Beacon Caveat One more thing. Beacon. For many years, character sheets existed in a particular form on Roll20, with predictable ways to access their data (called legacy sheets). Then Roll20 released Beacon, which while it promised sheet builders newer flexibility in the web dev space, imposed no parameters of a standard UI contract. As a result, any given sheet built with Beacon will employ a completely bespoke architecture of its data, with a completely bespoke method of accessing that data, subject to the whim (and ability) of the sheet developer to expose it. There's no guarantee that it will follow the same pattern as any other sheet -- be those Beacon or legacy sheets. For example, a sheet could be released with no attribute access at all if the developer(s) didn't build such. Fetch does what it does because it can agnosticize how it accesses game data. When sheets introduce bespoke data structures, bespoke data "objects", and bespoke access methods, they raise the likelihood of requiring bespoke scripts. Until either Beacon-built sheets follow a standard UI contract for all of this (even if it's different from legacy) or I can obtain some sort of 10,000ft-view of the beaco-sphere to find a signal in the noise, Fetch won't be able to return certain info from beacon sheets. It will still work for non-sheet objects (tables, pages, tokens, etc.), and it will still work for character *properties* (like tags, name, journal presence, or the name any player with controlling rights) and for character *attributes* and *abilities* (to whatever extent a beacon sheet might employ either of these last two), but it will not work for what are called "computeds". Unfortunately, the 2024 DnD 5E by Roll20 sheet is a Beacon sheet. Again, Fetch will work for the rest of your game data, and for *some* of the data around a 2024 character, but much of it will simply be unreachable. Every Object, Every Property All objects should now be reachable via a Fetch construction, if not multiple different Fetch constructions! As mentioned, Fetch should immediately make emergent properties and new object types available as soon as they are in your game. For instance, when Roll20 added a fourth token bubble to tokens, that was a new property on a known object type. Similarly, when they added map pins, those were a whole new object type with a whole new set of properties. Had this version of Fetch been available at those points, all of that would have been available to you immediately. (See Shortlist of Syntax Patterns, below) One note about emergent properties like bar4 for a token. As you'll see later in this post, there are properties that exist on the game object, and then there are property nicknames and pseudo properties that Fetch can provide. Nicknames Explained : For instance, a token has a property for bar4_value, bar4_max, and bar4_link. These are Roll20 properties. Fetch also provides nicknames for these as bar4, max4, and link4. Pseudo Properties Explained : Similarly, a token has a _pageid property (which has an alias of pageid) that (obviously) stores the ID of the page where the token resides. However, since the ID is unique and will always index the same page object, we can reliably return the name of that page object&nbsp; -- which could be a much more helpful bit of data since we don't go around referring to the pages by their IDs but by their names. So, beside the pageid property for a token, it also has a pseudo property of page, which will return the name of the page indexed by the token's pageid property. In the context of an emergent property or a new object type, while the Roll20 properties will be immediately available in your game, the nicknames and pseudo-properties will not be until Fetch receives an update. For instance, in the case of the addition of the fourth bar to tokens, were this version of Fetch installed in your game, all of bar4_value, bar4_max,&nbsp; and bar4_link would be available immediately. The nicknames of bar4, max4, and link4, on the other hand, would have to wait until a Fetch update could add those, since those are manual encodings of now-known property values. New Menu for Available Properties Even without thinking about the new properties that Roll20 might add to an object, there are already of lot of properties available to you! If you forget how to reference something, or just wonder what properties are available to you through Fetch, you can use the new menu to browse. From chat, run the command: !fetchprops ...to bring up the menu: The objects that show up in this menu are the objects that are in use in your game. You can see from the above example that I was in a game where I had no rollabletables, otherwise you would see an entry for the properties associated with that sort of object. To see the properties associated with a particular object type, click the "Props" button for that type to produce a menu listing the properties. Properties and their nicknames will all show up in the same row, letting you know you could use any of those handles to retrieve that piece of information: Getting an Object Type to Show Up As just stated, object types appear in this list if they exist in your game (that is, if you have created an object of that type within this game), but it does require one more step to cause Fetch to build the set of properties associated with that object. This could be for a "known" type such as rollabletables, or it could be for a novel type newly released by Roll20. There are 3 ways for that object type to appear in the above menu: Click the "Rebuild" button in the above menu (or the Props menu), A sandbox reboot, OR Directly reference the object of that type that you added to your game with a Fetch construction (see Shortlist of Syntax Patterns, below) For instance, imagine that at some point Roll20 releases a new object in the game with a type of "widget". Eager to see what you can do with it, you go to your game and run the command to produce the above menu. At this point, the widget object type won't show up in the output. So you add a new widget to your game, then click the "Rebuild" button from the menu. The menu regenerates, and this time the widget type is listed, along with the number of properties available to you, and a "Props" button to allow you to drill down and see the properties associated with the object type "widget". New Retrieval Options New Objects With Fetch 2.2.0, support is added for all object types, which crucially includes map pins. Pins are selectable (allowing them to be referenced by the "selected" keyword, see Shortlist of Syntax Patterns, below), and/or can be referenced by their ID or their name/title or sublink properties. New object availability is also added for things like attributes, abilities, and macros. Obviously you've always been able to get the value of an attribute or to get the action text of an ability/macro via specific syntax within Fetch, but now you can retrieve all of the data around the object itself (such as the istokenaction property of a macro): @(macro.MacroName.istokenaction) Token from Specific Page If you have multiple copies of a token-of-a-particular-name in your game, spread across different maps, and you need to refer to a specific instance of that token that is on a different page from you (i.e., "I want to refer to the token named 'Commoner' that is on a page named 'Game Library'"), you can use this new syntax pattern: @(Commoner[Game Library].someProperty) In that syntax, the page reference can either be a page name or a page ID. Obviously the token reference can be the same, but if you had the ID of the token you wanted to refer to, you wouldn't need this new syntax, so it's more likely that you would always use a token name with this syntax. Repeating List Retrieval Updates Repeating lists get more access options with this version of Fetch, too. Repeating lists are collections of attributes named with a similar naming structure that involves an ID for the row of items. For instance, a repeating list called "inventory" might have fields (or attributes) for itemname, weight, value, etc. I refer to these fields as "sub-attributes" of the row entry in the list. To retrieve the correct sub-attribute (i.e., to find the correct weight of a sword on the inventory list), you first have to access the correct row in the list (the "sword" row), then return the requested sub-attribute. Repeating List Row Selectors With past versions of Fetch, you were already able to use a pattern to select the entry from a repeating list (i.e., "give me the weight from the inventory list where the itemname is 'Sword'"): *(Bob the Hirsute.inventory.[itemname = Sword].weight) You were also able to use $0 numbering to access the row as it appeared on the character sheet ($0 would return the first row, $1 the second, and so on): *(Bob the Well Coiffed.inventory.$2.weight) $n &nbsp; Now, you can also use $n (or $N) notation to access the last entry in a list: *(Bob the Making an Effort.inventory.$n.weight) min?/max? As well, you can use min?/max? in combination with a sub-attribute to get the row representing the domain minimum or domain maximum value from the list in that sub-attribute. For example to get the name of the heaviest item in the inventory, you could do: *(Bob the Looking Good.inventory.max?weight.itemname) Or, to get the name of the lightest item on the inventory: *(Bob the Feeling Good.inventory.min?weight.itemname) The sub-attribute you supply for the min/max examination will be evaluated as if it contained a numeric value, so if you have non-numeric data in your that field somewhere in your repeating list, you might get unexpected results. 1dW or 1dWeight You can also use 1dw or 1dweight (capitalization does not matter) to get a random row entry from the list, from which you can return a specific sub-attribute. *(Bob the Rico.inventory.1dw.itemname) This will give every entry on the list the same chance of retrieval. If, instead, you want to weight certain items from the list, you can reference a sub-attribute as a part of the 1dw row selection syntax by adding it after a question mark: *(Bob the Suave.inventory.1dw?weight.itemname) That would return the itemname from a random entry from the list where an item with weight of 10 would have 10x the chances of being returned compared to an item with weight of 1. If you would rather refer to the max value of a subattribute, use another question mark followed by the word "max". With data points limited in a repeating list (you can't just add a new column/field to the set of sub-attributes associated with an entry in the list), I have found use for this by leveraging the "max" value of the name of the list entry. In other words, it's rare that on the inventory list I would ever have to refer to the "max" value of the itemname sub-attribute, so this would be a good place to stash a "random weight" value for the entry. Then I could get a weighted-random return using that max value like: *(Bob the Shut the Front Door.inventory.1dw?itemname?max.itemname) TIP: If you want to go this route but your sheet does not give you easy access to the max entry for any given sub-attribute on your list, you can use the ChatSetAttr script to set all of the max values for whatever sub-attribute you choose for every item in your list at once. That is, Bob could set the max value for each entry's itemname sub-attribute in one command. This is made even easier by the ability to return rowids for the list, as discussed, below. avg?/sum? Use avg? or sum?, in combination with a sub-attribute, to get the average or sum of a given field across the list. For instance, to get the average itemvalue from an inventory list you would use: *(Bob the Thirst Trap.inventory.avg?itemvalue.itemvalue) Note that you still must use the sub-attribute you are averaging (or summing) as the sub-attribute you wish to return. vals | uniq | rowids | ids These return a delimited list of data from across the repeating section. For instance, you could get a comma-separated list of itemnames from your inventory. The functions are listed below, along with an explanation of what they return: vals | list of the current values of the requested sub-attribute; to return a list of the max values, use ".max" after the sub-attribute name uniq | same as vals except that the list is limited to unique returns rowids | list of the rowids in the list (rowids tie sub-attributes together into a single list entry) ids | list of the ids of the attributes identified by the sub-attribute name provided To use a different delimiter than a comma, follow the function word with a question mark, then your preferred delimiter: *(Bob the Heavenly.inventory.rowids?|.itemname) TIP: rowids is a way of getting the ID that ties sub-attributes into a single list entry. As mentioned, it could be useful in constructing a ChatSetAttr command to target some sub-attribute in every item on the list. Using the rowid of an entry, you can reconstruct the name of any attribute associated with the list. The attribute name is built as: repeating_listname_&lt;rowid&gt;_sub-Attribute So if one of the returned rowids from a list named "inventory" was -M1234567890abcdef, then the itemname sub-attribute in that row entry will be named: repeating_inventory_-M1234567890abcdef_itemname Hand a series of those off to ChatSetAttr and you can quickly set all of the fields for a given sub-attribute across all the items in a list.
1779216920

Edited 1779283024
timmaugh
Pro
API Scripter
Shortlist of Syntax Patterns Getting a Token If a formation, below, calls for a "Token Ref" (a token reference), that can be any of: a token's name a token's ID the word 'selected', if the token is the first token in the set selected of selected tokens for the message the word 'tracker', with or without an offset (for more info, consult the Using the Tracker section of this post ) Getting a Character If a formation. below, calls for a "Char Ref" (a character reference), that can be any of: any of the Token Refs, provided the referenced token represents a character a Character's name a Character's ID Nesting Formations Fetch constructions are nestable and will resolve from the inside out. This allows you to use one Fetch construction to return a value that another Fetch construction will require. For instance, you could use the cardid property of a selected graphic to get the card object, and from the card object, use the deckid to get the name of the deck to which the card belongs: cardid of selected | @(selected.cardid) deckid of that card | @(card.@(selected.cardid).deckid) name of that deck | @(deck.@(card.@(selected.cardid).deckid).name) However, check to see if the property you want might be available to you directly from the object you have. These situations have been widely detected and already built into the objects where you would want them. In the above example, you are basically looking for the name of the deck associated with a graphic (which you have selected) that represents a particular card (ie, a card that has been played to the VTT). This datapoint has been established as a pseudo-property of a graphic, already: @(selected.deckname) Much simpler. Syntax FOR PATTERN EXAMPLE Token Property @(Token Ref.property[optional default]) @(selected.bar1) Token Property using Page Ref @(Token Name[Page Ref].property[optional default]) @(Bob[Character Library].id) Character Property @(Char Ref.property[optional default]) @(Bob.player_name[no player attached]) Character Attribute Current @(Char Ref.attribute) @(Bob.hp) Character Attribute Max @(Char Ref.attribute.max) @(Bob.hp.max) Character Ability %(Char Ref.abilityName) %(Bob.CleansingRitual) Character Repeating Attribute ...using pattern *(Char Ref.list.[pattern].subAttribute) *(Bob.inventory.[itemname ~ Mail].itemweight) ...using $0 through $n notation *(Char Ref.list.$#.subAttribute) *(Char Ref.list.$n.subAttribute) *(Bob.attack.$2.atkname) *(Bob.attack.$n.atkname) ...using 1dW with equal weight *(Char Ref.list.1dw.subAttribute) *(Bob.inventory.1dw.itemname) ...using 1dW with sub attribute for weight *(Char Ref.list.1dw?subAttribute1.subAttribute2) *(Char Ref.list.1dw?subAttribute1?max.subAttribute2) *(Bob.inventory.1dw?itemweight.itemname) *(Bob.inventory.1dw?itemname?max.itemname) ...using avg? or sum? with sub attribute for aggregation *(Char Ref.list.avg?subAttribute1.subAttribute1) *(Bob.inventory.avg?itemweight.itemweight) ...using vals, uniq, rowids, or ids *(Char Ref.list.vals.subAttribute) *(Char Ref.list.vals?delim.subAttribute) *(Bob.inventory.vals.itemname) *(Bob.inventory.vals?|.itemname) Macro #(MacroName) #(ResetNPCs) Property of Object Using Object Type @(ObjectType.Object Ref.property[optional default]) @(handout.Mysterious Note.avatar) Property of Object Using Object ID @(ObjectID.property[optional default]) @(-M1234567890abcdef.avatar)