Below is a list of changes to macros in the current Subversion Source. If you have the code checked out, any testing/feedback would be appreciated. Text in green discusses small fixes in patches that either have not been applied or not sent to Trevor yet
Firstly a warning about compatability.
There are some incompatabilities in b51 concerning macros (but they are not large and it is easy to change your macros to take them into account). I know this whole post is too long to read so I am putting these up the top. There are other changes but they should have little effect, but all bug fixes that affect macros are listed at the bottom of the post in case.
getProperty() function.
Because this has caused so much confusion and there have been so many requests for a change to it, it has been changed. The getProperty() function will now evaluate the contents of the property/fetch the default if it is empty and there is a default value. The old behavior is available using the getRawProperty() function.
JSON changes.
JSON objects and arrays are now kept in an internal format that is a lot faster to process and converted to strings for output or storage in properties. In most cases these changes will be completely invisible to you other than seeing some speed increases when using large JSON objects. There are two cases where you will notice it though
- Using if to compare JSON objects.
Most commonly used to compare json arrays/objects to other arrays/objects or check if they are empty (there are really no other valid comparisons you could do ). If you for some reason must use the json object as a string you can use the string() function to turn it into one (e.g. if(string(json) == "") )- For empty comparisons use if(json.empty(json))
- For comparisons to other json object/arrays use if(json.equals(j1, j2))
- Appending JSON objects to strings
If you want to append a JSON object to a string you will now have to use the string() function to turn it into a string, e.g. ["My JSON Object is '" + string(json) + "'...")
Changes
- getTokens()/getTokenNames() now can accept a JSON object with multiple conditions that need to be fulfilled as the second argument.
The valid keys for this JSON object are (all fields optional)- setStates - Takes a JSON Array, only tokens with these states set are returned.
- unsetStates - Takes a JSON Array, only tokens with these states not set are returned.
- npc - true/false
- pc - true/false
- selected - true/false
- impersonated - true/false
- current - true/false
- owned - true/false (owned by the player)
- visible - true/false
- range - JSON object
- area - JSON object
- token - token id/name, if not specified range is from the current token
- distancePerCell - true/false, should the distance per cell multiplier be used.
- from - number, find tokens from this distance (inclusive)
- upto - number, find tokens upto this disatance (inlclusive)
For example
To get NPC tokens that the player (client) can see that are not dead, returned as a JSON array.Or to get the same list but only with tokens in adjacent squares (or hexes)Code: Select all
[getTokens("json", '{npc:true, unsetStates:["Dead"] }' )]
The area option is a little more complex than the other options. It allows you to define an arbitarily shaped "area", and only tokens in this area are returned.Code: Select all
[getTokens("json", '{npc:true, unsetStates:["Dead"], range: { from:1, upto:1, distancePerCell: 0} }' )]
The format for area is a json object with the following fields.- token - token id/name (optional) the token id or name at the center of the defined area. If no token is specified the current token is used.
- offsets - An array of JSON objects with x and y fields. These are offsets (in cells) from the token at the center of the area, these offsets define which cells are within the area.
Code: Select all
[h: left = '{x:-1, y:0}'] [h: right = '{x:1, y:0}'] [h: up = '{x:0, y:-1}'] [h: down = '{x:0, y:1}'] [h: area = json.set("", "offsets", json.append("", left, right, up, down))] [h: query = json.set("", "npc", "true", "area", area)] [getTokens("json", query)]
- json.get() can now be used to return json array slices
Use json.get(array, first, last), these return a json array.
For first 4 values in array.Negative numbers can be used as offsets from the end of the array, -1 is the last element in the array, -2 second last, and so on.Code: Select all
[json.get(arr, 0, 3)]
So to get all but the first element in a arrayTo get the last two elementsCode: Select all
[json.get(arr, 1, -1)]
If the end index is smaller than the start index then the array slice is returned in reverse. For exampleCode: Select all
[json.get(arr, -2, -1)]
Returns [7,6,5,4] (index 6 down to index 3).Code: Select all
[json.get('[1,2,3,4,5,6,7,8,9,10]', 6, 3)]
- json.get() can now be used to return multiple fields of a JSON object
The value that is returned is another JSON Object.
For exampleReturns the json objectCode: Select all
[json.get('{ a:1, b:2, c:5, d:10 }', "a", "c")]
Code: Select all
{ a:1, c:5 }
- method="json" can be used in a <form> tag for dialogs and frames.
When the form submission calls the macro the values in the form will be passed to it (via macros.args) in a json object instead of a string property list. This method is probably more useful as there is less problems with what characters the user can type in. - createMacro(), setMacroProps()
When using the JSON form of these macro functions it is now possible to specify the command, for example.Code: Select all
[createMacro('{ label:"test", command:"This is a test, now have a dice roll [1d6]"}')]
- Macro Functions that can reference other tokens
The following macro functions can now accept an optional last parameter which is the token to get the the information from or to modify. You can only specify the token if the macro is trusted.- hasMacro(macro [, id])
- getMacros([delim [, id]])
- getMacroProps(index [, delim [, id]])
- getMacroIndexes(label [, delim [, id]])
- getMacroCommand(index [, id)]
- createMacro(label, command [, props [, delim [, id]]])
- createMacro(json [, id])
- setMacroProps(button, props [, delim [, id]])
- setMacroCommand(index, command [, id])
- removeMacro(index [, id])
- command (json only)
- getGMName([id])
- getHalo([id])
- setHalo(halo, [id])
- getState(state [,id])
- setState(state, val [,id])
- setAllStates(val [,id])
- getVisible([id])
- setVisible(val [, id])
- getPropertyType([id])
- setPropertyType(type, [id])
- getPropertyNames([delim [, id]])
- hasProperty(name [, id]])
- isPC([id])
- isNPC([id])
- setPC([id])
- setNPC([id])
- getLayer([id])
- setLayer(layer [, id])
- getSize([id])
- setSize(size [,id])
- getOwners(delim [, id])
- isOwnedByAll(delim [,id])
- isOwner(player [, id])
- resetProperty(prop [, id])]
- setProperty(name, val [, id])
- getProperty(name [, id])
- isPropertyEmpty(name [, id])
- bringToFrong([id])
- sendToBack([id])
- getLabel([id])
- setLabel([id])
- The following functions can now be called by players in trusted macros
- getCurrentInitiative()
- setCurrentInitiative()
- addAllToInitiative()
- addAllPCsToInitiative()
- addAllNPCsToInitiative()
- setInitiativeRound
- nextInitiative() (when not the token owner)
- setInitiativeRound()
- removeAllFromInitative()
- removeAllPCsFromInitiative()
- removeAllNPCsFromInitiative()
- addToInitiative() (when not the owner)
- removeFromInitiative().
- [dialog(...): ...] and close buttons
The second argument can contain closebutton=0, or you can addTo disable remove the close button for dialogs. Unlike setting the dialog to input this will not close the dialog once a form has been submitted. So you can choose to keep it open or close it with closeDialog() if you only want to close it based on certain criteria.Code: Select all
<meta name="closebutton" content="false">
Using the closebutton and input together are not supported so don't do it! - JSON object speed
large JSON objects should be faster to use than they were before. If you have large lists you should move them to JSON arrays (JSON arrays were already faster but now they will be a LOT faster). - Assert function.
Assert now has an optional 3rd argument assert(test, message [, showPrefix])
If a 0 is passed the message prefix for the assert error "Macro defined error: "
is not displayed
New Additions
- You can now specify a tool tip for macro buttons in the macro button dialog. ToolTips that start with { or [ are evaluated, so you can create tool tips that represent uses remaining etc.
- New macro function removeMacro(index, [, id]) to remove the macro button on a token.
- arr = json.sort(array [, direction]) function can be used to sort json arrays. Direction is ‘ascending’/’descending’ (or any part of so ‘asc’/’desc’/’a’/’d’ all work). If arrays contain just numbers they are sorted in numerical order otherwise as strings in alphabetical order.
- arr = json.shuffle(array) randomly shuffles the values in a json array.
- arr = json.reverse(array) reverses the order of the elements in a json array.
- json.indent(obj, [spaces]) “pretty formats a json string for debugging purposes, if outputting to chat use
Code: Select all
<pre>json.ident(jobj)</pre>
- getRawProperty(prop [,id]) gets the unevaluated value of a property, returns “” if property is empty (not the property default).
- getInitiativeToken() [trusted] returns the id of the token that currently has the initiative.
- getTokenStates([delim]) gets a list of the valid states in the campaign, “json” as the delimiter will return a json array.
For the following functions useDistancePerCell defaults to true (1) if it is optional. If it is true the the distance or location is returned using the "Distance Per Cell" measurement when setting up the map. If it is set to false (0) then the measurement is in cells. For maps with no grids strictly there are no cells but the same logic applies.
For maps with grids, the distance returned is for the closest two cells that the token occupies if either token is more than 1 cell in size. For non grid maps its always from the center of tokens.
Two things you need to keep in mind. For hexes at the moment Tokens don't always occupy the hexes they look like they are occupying. For non grid maps even though your token may not look it, the image for the token is always a square, so for top down tokens your center may be slightly different to where you expect it to be, but to make any real difference you would have to have a REALLY big token.
- getDistance(target [, useDistancePerCell [, sourceId]])
This is a trusted macro function that will return the distance between two tokens.If the source token is not specified then the distance is returned from the current token. - getDistanceToXY(x,y, [, useDistancePerCell [, sourceId]])
Gets the distance to a target x,y co-ordinate. If the source token is not specified then the distance is returned from the current token. - getTokenX([useDistancePerCell [,token]])
Gets the x co-ordiante of the token.If the source token is not specified then the distance is returned from the current token. - getTokenY([useDistancePerCell [,token]])
Gets the y co-ordiante of the token.If the source token is not specified then the distance is returned from the current token. - getTokenZ([useDistancePerCell [,token]])
Gets the z order of the token.If the source token is not specified then the distance is returned from the current token. useDistancePerCell for now is totally and utterly ignored, and probably will always be.
Changed to reflect IMarvinTPA comments now getDrawOrder([token]) - moveToken(x,y [,z [useDistancePerCell])
Moves the token to a new location. If you want to just move to a new x,y and keep the same z order speciy -1 as the z order.
Changed to reflect IMarvinTPA comments now
moveToken(x,y [, useDistnacePerCell [, token]])
and
setDrawOrder(z [, token] - getMacroGroup(groupName [, delim [, id]])
Gets the indexes of the macro buttons for the specified group. If delim is “json” then a json array is returned. If the id of a token is specified then the function can only be run from a trusted macro. - setTokenFacing(angle, [,id])
Sets the facing angle of the token, if a token id is specified then the function can only be created from a trusted macro. - getTokenFacing([id)
Gets the facing angle of the token (or “” if there is no facing), if a token id is specified then the function can only be created from a trusted macro. - removetokenFacing([id])
Removes the facing from a token, if a token id is specified then the function can only be created from a trusted macro. - json.isEmpty(val)
Returns 1 if val is an empty JSON object, or JSON Array, or an empty String (as that can be used as a json object in some functions). Otherwise it returns 0 (even for non JSON objects). - json.equals(j1, j2)]
Returns 1 if the two json objects or arrays are equal. - json.evaluate(obj)
json.evaluate() is a trusted only function that can be used to traverse a json object or array and evaluate any macros/rolls contained within agaisnt the current token and variables.
For example, you could store the details for powers in a json object such asThen you could have a macro in a lib:token that takes this information, uses json.evaluate() and format it as you like, e.g.Code: Select all
{ name: "Wet Noodle Attack", attack: "Str vs AC = [1d20 + StrMod + MiscAttBonus]", damage: "[WeaponDamge + StrMod + MiscDamBonus]" }
You may of course want to do some nicer formating than this Or even use it for something completely different.Code: Select all
[h: att = json.evaluate(power)] You attack with [r: json.get(att, "name")]<br> Attack Roll is [r: json.get(att, "attack")]<br> Damage is [r: json.get(att, "damage")]<br>
If json.evaluate() finds a an object or array within the object/array it will also evalute any values in these objects/arrays.
User Defined Functions (lib:token short hand)
You can now define functions that can be called from within [ ] and { } as you would call normal functions. There are several new functions to support this.
- defineFunction(function, tokenFunction)
defineFunction() can only be called from trusted macros and can be used to create GM defined functions, (or shortcuts to lib:token functions if you prefer to think of it that way). - isFunctionDefined()
Returns 1 if a function has been defined by defineFunction() or 2 . - val = arg(number)
Used to get the specified argument from within a defined function. Argument numbers start at 0. - no = argCount()
Returns the number of arguments passed to a defined function.
You create a macro on your Lib:Tokens called "onCampaignLoad". When a campaign is loaded (either via load or connecting to a server that has the campaign loaded). All trusted lib:tokens are checked for a macro named "onCampaignLoad" and if one exsts it is executed (output is discarded). From these macros you can use defineFunction() to define your own functions that point to macros on the lib:token.
You can then call the use defined function as you would any other function. e.g. [myfoo(“test”, 1,2,3)].
Any output (not macro.return) the macro generates is the return value of the function, (special magic happens to convert to number if appropriate). If you assign a value to macro.return inside of the function it is available to the calling function.
Note: at the moment it always returns a string, and comments in the function get copied into the string. In next patch I send to Trevor if the output is a number then its returned as a number, comments are not inserted. Also if there is no output from macro (everything is in [h:] or comments then the value assigned to macro.return is returned.
Centering the Map
- goto(token)
Centers the map on the specified token. - goto(x,y)
Centers the map on the specified x,y location (cells as per the /goto command)
- json.contains now works for json arrays and not only for json objects.
- Removed a lot of null pointer bugs in macro functions.
- Corrected a lot of error messages in the macro functions so now all errors should reflect the function the occurred in.
- Players can no longer call removeAllFomInitiative(), removeAllPCsFromInitiative(), or removeAllNPCsFromInitiative(). But these functions can now be called from trusted macros.
- Getting a token property (via either getProperty() or val = property) will now always return its default value (if a the property is not set and it has one). Previously this would only occur if the token had been been edited with the edit token dialog, which lead to inconsistent behavior.
- The macro functions to edit token macros now check the player editable status of the button before they will allow the player to edit them. If you need to edit a not player editable macro button from macro then you will have to put it into a trusted macro.
- Fixed a bug with specifying the height of a dialog in a macro using [dialog(...): ...]
- Updated macros so that tokens are always repainted.
- The parser is now a log less zealous about trying to evaluate things. Values in variables should no longer be evaluate by just reading the variable. Note: this is not true of values in properties, reading them will evaluate them. If you wish to get an unevaluated value from a property use getRawProperty(). So now it is save to do things such as a = “[1d20]” without fear of interference from the parser. Use evalMaco()/execMacro() if you do want to evaluate these strings.
- The parser now trys to convert single element JSON arrays to arrays before trying to parse them. So single element JSON arrays will no longer give you head aches.
- Macro dialogs and frames no longer autoscroll the the bottom, the scroll bars will be positioned to show you the top left hand corner of the dialog/frame.
- json.get() now returns an empty string if the value is not set not a null.
- [macro(...):...] can now call macros from @campaign, @global, and @token (the impersonated token).
- macroLink() and macroLinkText() functions can now use @this, @campaign, @global, and @token.
- getImage(), getTokenImage(), getPortrait(), tblImage(), tableImage() no longer limit the max image size to 500.
- When clicking on macro buttons macro.args is set to “” by default (no more unwante prompting when clicking on those buttons that are either clickable or callable via [macro(...):..]
- JSON Objects now work in macro links.
- currentToken(), getImpersonated(), getImpersonatedName(), getSelected(), getSelectedName(), getVisible(), getVisibleNames() functions no longer need to be in a trusted macro as the player already knows the tokens exist.
- Image: and Lib: utility tokens are not returned from the getToken functions.
- When ever an exception occurs during a macro the error message will now always return either the name of the macro function that failed, or the line that failed (so no more vague cant execute: null, or cant execute: Java.lang.String error messages).
- Typing carriage returns into <textarea> will no longer cause the macro.args string to break.
- Macro function setHasSight() can now be called on PCs to match changes elsewhere.
Edited to fix formatting
Edited with updates