Script:SmartAoE
From Roll20 Wiki
Page Updated: 2021-11-20 |
Main Page: API:Script Index
Concept
SmartAoE. Have you ever overlayed a cone, line, or circle template on a map and wondered if that square that is just barely covered should count as "in"? Sure, there are pre-made grid templates out there, but you are usually limited to 90/45deg orientations. The idea behind this script is to provide flexibility in casting angles (to align just the right spot before triggering) and a means for consistent ruling of these cases. Also, the script can detect creatures within affected squares and be able to roll saving throws and/or apply damage and condition markers automatically (ala GroupCheck or similar script).
Figure description. Example of 4 active AoE's: 90deg cone (fixed radius, snapped to intersection), 5e-style cone (variable radius), line, and circle (using float keyword and custom multi-sided controlToken). The circle AoE is triggered (5e SRD Ice Storm spell), making saving throws for tokens in area, automatically deducting damage (--autoApply|true
), and outputting results to chat.
How does it work?
Current functionality is to spawn a "control token" over a selected token. The location of the AoE is set by the positioning of the control token (and for lines/cones, the relative positions of the selected and control tokens). Depending on the commands used to spawn that control token, the script will associate those settings with that control token and act accordingly (a link between the selected and control tokens is made and stored in persistent memory). To determine whether a square is affected, you define a minimum grid coverage area in your macro. I'll use cones in the following discussion: For example, using --minGridArea|0.25
would require 25% of the grid square to be covered by the cone boundaries. You can choose to have the origin of the cone be in the default center of the origin (selected) token, or use the --origin|nearest
or --origin|nearest, face
command to automatically shift the origin point to the nearest corner or face of the origin token. There are additional commands to rotate the cone origin point CW/CCW and to "trigger" the effect.
You can also define rules for the minimum percentage of a token's area that needs to be covered by an affected AoE grid square in order for the creature to be considered "in" the area of effect.
There are a variety of customization options for the AoE, including type (line, circle, cone, square - of which circle and square can be made independent of source token position), radius, AoE fill and line colors, saving throw formulas, difficulty class (target save number), up to two damage formulas & damage types, condition markers for pass/fail/dead, bar links, damage resistance/vulnerability/immunity (based on attribute content and damage type), auto application of damage, and auto-deletion of the AOE controlToken and/or "dead" tokens. These parameters and more are described below under #List of Subcommands.
If the AoE definition includes any rolls (for damage or saving throws), there will be customizable chat output with the results.
- What about hidden tokens? Results for Tokens on the GM layer will be whispered to the GM in a separate table
- What if I want to hide the names of tokens to avoid? There is an option
--hideNames|1
to keep your chat spoiler-free :)
Initial Setup/Install
- Install the SmartAoE api script, currently found on GitHub here: v0.22a
- Install the API script (available as 1-click install). SmartAoe is dependent on this utility script!
- Note: the following two steps are now automatically performed for you (if the "AoEControlToken" character does not exist during install) if using version 0.21 or later!
- Create a character sheet with the default token image you want for the AoE control token. If you do not specify a
--controlTokName
, then the script will look for the default token for a character namedAoEControlToken
. You can use a Multi-Sided Token if you'd like, for which you may set the side and size via commands when spawned. - Give the default token sight. A while ago, Roll20 decided to give sighted tokens z-order priority over sightless tokens. If the control token does not have sight, it may appear under the caster token, requiring moving the caster token out of the way to access it. Giving the control token sight will eliminate this possibility.
- Create a character sheet with the default token image you want for the AoE control token. If you do not specify a
- Create macro(s) utilizing the syntax described below. There are currently six different base commands, though the first and third will be the most common:
Syntax Description !smartaoe <subcommands> this is the primary api syntax. Descriptions of the subcommands are found here (#List of Subcommands), but they all follow the form --commandName|parameter(s)
!smartrotateorigin <cw/ccw> valid for cones only - this rotates the origin point cw or ccw along corners or faces of the origin token !smarttrigger Triggers the fx and does all the stuffs. Currently no subcommands for users (though the script itself may call this command with params via a !smartquery below). !smartquery <optional token_id> This checks a selected token (if no token_id parameter supplied) or a targeted token (requires a token_id parameter) for overlapping AoEs and triggers any/all AoE's that are active in the target token's square only. Useful for effects that occur at the start or end of a creature's turn (e.g. 5e Stinking Cloud or Flaming Sphere) !smartremove currently no subcommands. All of the AoE's (and associated controlTokens) linked to the selected token will be removed. This is primarily used for AoE's with fixed radius emanating from a caster. See the subcommand --controlTokName|self
in the #List of Subcommands below for more details!smartclearcache currently no subcommands. All of the AoE links in the Campaign will be removed. This will not delete any AoE tokens or paths (drawings), which will need to be deleted manually. Only use this command if you suspect that the SmartAoE State object has been corrupted somehow.
It is recommended to create the following abilities on the AoEControlToken character and select "Show as token action". This will expedite the AoE manipulation and triggering process. Note: Installing v0.21 or later will automatically create the "AoEControlToken" character if it does not already exist, and it will also create the following abilities as token actions for that character! |
Ability Name Ability Content Description CCW !smartrotateorigin ccw rotates a cone origin point counter-clockwise CW !smartrotateorigin cw rotates a cone origin point clockwise Trigger !smarttrigger triggers the AoE effects in every affected grid square
If the caster character utilizes spells/effects centered on themselves rather than a controlToken, it is recommended to create the following abilities on their character sheet and select "Show as token action". This will expedite the AoE manipulation and triggering process. I've prepended the ability names with a lowercase "a" to sort them to the beginning of the token action bar for easy reference |
Ability Name Ability Content Description aTrigger-ALL !smarttrigger triggers the AoE effects in every affected grid square aTrigger-Target !smartquery @{target|Choose a Target|token_id} prompts for a target token and triggers the AoE effects only on that token aRemove !smartremove removes all AoE tokens and grid shading linked to the selected caster token
Something else that is automatically generated during install (v0.21+) is a collections macro named "AoEGenerator". This macro creates a chat menu containing simple versions of all of the AoE types possible with this script. Feel free to trim this down to only include aoeTypes that are relevant to your table (if playing 5e D&D, you won't need the Pathfinder-types, for example). |
Acknowledgements
- Folks I've shamelessly stolen and/or modified code from:
- TheAaron (numerous sources)
- Kurt J. (chat styling, Bresenham line algorithm Scriptcards)
- Jakob(GroupCheck)
- keithcurtis(ping-pull and api triggers from from chat icons Reporter(Forum))
- All-around great guy whose brain I've picked too many times to count: timmaugh
- My home game players and my Patreon supporters for beta testing and feedback, with special thanks to FR and Michael C
Disclaimer: Patreon campaigns are not affiliated with Roll20. Contributions are entirely voluntary and Roll20 cannot provide support or refunds for contributions.
List of Subcommands
AoE Behavior Commands
Command | Parameter(s) | Description | Default Value | Example(s) | Additional Notes |
---|---|---|---|---|---|
--radius | <###><optional units like ft. or "u"> | If a numeric value is specified, the AoE will have a constant radius regardless of the distance between the origin and control tokens. If "variable" (or omitted), the radius is dynamically determined by the relative spacing of the two tokens | variable | --radius|60ft --radius|12u |
If no units are given, will default to pixels! Behavior depends on "aoeType" keyword and "origin" type: For "nearest" origin, if "float" keyword is omitted, the origin token's square is omitted from the radius (making it bigger). Otherwise, float will result in a true radius. |
--aoetype | <type>,<optional coneAngle> or <optional "float"> | currently supports "line", "square", "circle", "PFcircle" "cone", "PFcone", and "5econe" | line | --aoetype|5econe --aoetype|cone, 90 --aoetype|circle, float --aoetype|line --aoetype|PFcone |
--The "float" keyword "untethers" the AoE from the sourceToken, creating the AoE around the controlToken only (e.g. a Fireball spell) --"5econe" will create a triangular shape whose max width equals its length, whereas a "cone" will use a true radius and coneAngle --The "PFxxxx" types will perform an additional filtering on affected grids using the "every other diagonal counts as 2squares" rule. NOTE: default is "line" if this subcommand is omitted. |
--instant | <true/yes/1/false/no/0> | If set to true, the selected AoE token will be deleted immediately after triggering | FALSE | --instant|1 | NOTE: default is false if this subcommand is omitted |
--origin | <style> <optional "face"> | AoE starts in center or edge of square (ignored for line AoE’s) | center | --origin|nearest --origin|nearest, face --origin|center |
"Center" (or omitted) will pick the center of the origin token for the AoE origin. "Nearest" will pick the closest corner. "Nearest, face" will include the flat sides of the token's square as potential origin points. |
--forceIntersection | <true/yes/1/false/no/0> | forces the dragged controlToken center to the intersection of grid squares | false | --forceIntersection|0 | I'd recommend setting to true for PF-style cone approximations. Otherwise, if set to false (or omitted), you can always press Alt while positioning the controlToken for fine tuning of the AoE placement. NOTE: If this command is omitted, the default behavior is to automatically set to true if a "float" style circle or square AoE is used, unless the total AoE size is 1 square or less (as you typically want a 5ft square to be centered in the square). You can always override this default behavior using the command explicitly however. |
--mingridarea | <#> | what percentage of the grid square’s area must lie within the AoE to be included in the final affected area | 0.01 | --mingridarea|0.1 | The example to the left would require only 10% of the grid to be covered by the AoE to be included. For 5e cones, I'd recommend a good place to start would be 25%. For PF cone approximations, I'd go with 50% along with --forceIntersection|1. More than 50% might start making cones not "emanate" fully from the source token |
--mintokarea | <#> | what percentage of the token’s area must lie within the AoE to be included as an affected creature | 0.01 | --mintokarea|0.25 | The example to the left would require only 25% of the token's area to be covered by affected squares for them to be in the area of effect. |
--fx | <style>-<type> | will spawn fx in affected squares when triggered | --- | --fx|burn-death --fx|glow-holy |
fx will spawn at each affected square |
--aoecolor | <#RRGGBBTT> | determines the fill color of the AoE’s affected squares | #ff000050 | --aoeColor|#ff000050 | NOTE: default is #ff000050 if this subcommand is omitted |
--aoeOutlineColor | <#RRGGBB> | determines the line color of the AoE outline | #ff0000 | --aoeColor|#ff000050 | NOTE: default is #ff0000 if this subcommand is omitted |
--gridcolor | <#RRGGBBTT> | determines the stroke(line) color of the AoE’s affected squares | #000000 | --gridColor|#ff994450 | To have no grid lines, set transparency to max by adding "00" to digits 7&8 NOTE: default is #000000 if this subcommand is omitted |
--controlTokName | <charName> or "self" | The name of the character sheet whose token will be used as the AoE control token. If a character name is provided, the default token will be spawned for later positioning. If "self" is used, the AoE will immediately expand around and move with the selected token | AoEControlToken | --controlTokName|Fireball --controlTokName|self |
It is generally best if the default token image is a png file with partial transparency to allow the affected grid squares and tokens to be visible. Rollable table tokens are supported. NOTE: "self" can only be used for square/circle aoeTypes with the float keyword and with pre-defined radius NOTE: if "self" is used, then --controlTokSize and --controlTokSide will be ignored.NOTE: if "self" is used, it is recommended to set --forceIntersection|0 to prevent the default behavior of centering float AoE's at grid intersectionsNOTE: default is "AoEControlToken" if this subcommand is omitted. |
--controlTokSize | <#> | The size (in squares) of the control token | 1 | --controlTokSize|8 | NOTE: default is 1 if this subcommand is omitted |
--controlTokSide | <#> | For rollable table tokens: the side of the control token's image to be set when created | 1 | --controlTokSide|6 | NOTE: default is 1 if this subcommand is omitted |
--isDrawing | <true/yes/1/false/no/0> | Sets the isdrawing property of the spawned control token | FALSE | --isDrawing|true | NOTE: default is false if this subcommand is omitted |
--ignore | <attrName, attrValue> | Characters with matching attribute values will not be included in the chat output | --- | --ignore|SmartAoE_Ignore, 1 | NOTE: must have both the attrName and attrValue separated by a comma |
--selectedID | <token_id or character_id> | allows direct selection of the source token | --- | --selectedid|-MlBqIRAtRSwAZ-8Jr_E | |
--playerID | <player_id> | The name of the calling player. Useful when calling from another API script. Tells the script which playerID can control the control token (if not set by default in character sheet). Also for error msg whispers when called by another script. | --- | --playerid|-MlBqIRAtRSwAZ-8Jr_E |
AoE Math and/or Trigger Behavior Commands
Command | Parameter(s) | Description | Default Value | Example(s) | Additional Notes |
---|---|---|---|---|---|
--dc | <value or inline roll> | the target roll for each saving throw (>= is passing, < is failing) | 0 | --dc|{selected|spell_save_dc} --dc|[[5d6+2]] |
|
--saveformula | <inline roll with special syntax (see notes)> | Formula to be used for saving throws for all affected tokens. | --- | --saveFormula|<<1d20 + a{dexterity_save_bonus}>> --saveFormula|5eDEX |
In order to delay the save rolls until the AOE is triggered, and to apply that formula to each token individually, replace inline roll format in the following way:
NOTE: for 5e games, you can use the shorthand "5eSTR", "5eDEX", "5eCON", "5eINT", "5eWIS","5eCHA" |
--damageformula1 | <inline roll> | Formula to be used for damage1 | --- | --damageformula1|[[8d6]] --damageformula1|[[(5+?{Cast at what level? |3,3|4,4|5,5|6,6|7,7|8,8|9,9})d6]] --damageFormula1|[[-1*2d6]] |
Negative damage will be interpreted by the script as healing! |
--damagesaverule | <expression> | The math operation (accepts single Operator and Operand) to be applied if the saving throw succeeds | *0.5 | --damagesaverule|*0 --damagesaverule|-10 |
NOTE: default is "*0.5" if this subcommand is omitted |
--damagetype1 | <text> | the damage type for damage1 | --- | --damageType1|Fire | |
--damageformula2 | <inline roll> | Formula to be used for damage2 | --- | --damageformula1|[[8d6]] --damageformula1|[[(5+?{Cast at what level? |3,3|4,4|5,5|6,6|7,7|8,8|9,9})d6]] |
|
--damagetype2 | <text> | the damage type for damage2 | --- | --damageType1|Fire | |
--bar | <#> | which token bubble to apply damage. Default = 1 | 1 | --bar|1 | |
--autoapply | <true/yes/1/false/no/0> | Toggle to apply damage and condition markers automatically or after subsequent interaction with chat output. | FALSE | --autoApply|1 | Confirmation of the applied damage will be posted to chat. Tokens on GM layer will have whispered confirmation. NOTE: default is false if this subcommand is omitted. |
--conditionfail | <name(s) of status markers to apply on a failed save> | comma delimited | --- | --conditionFail|red --conditionFail|red@3 --conditionFail|skull,cobweb |
NOTE: if custom markers are used, you must include the whole id, e.g. "name::####" NOTE:inserting @# will overlay a number over the condition marker |
--conditionpass | <name(s) of status markers to apply on a successful save> | comma delimited | --- | --conditionPass|Target::1510119 --conditionPass|green@3 --conditionPass|aura,bolt-shield |
NOTE: if custom markers are used, you must include the whole id, e.g. "name::####" |
--zerohpmarker | <name(s) of status markers to apply if designated token bar is 0 after damage is applied> | comma delimited | --- | --zerohpmarker|dead | NOTE: if custom markers are used, you must include the whole id, e.g. name::#### |
--removeAtZero | <true/yes/1/false/no/0> | if the key bar value is 0 or less after trigger, that token will be deleted from the map | FALSE | --removeAtZero|true | NOTE: default is false if this subcommand is omitted. WARNING: be careful with this, because if your player tokens are not ignored by the AoE (using the --ignore command), their tokens will be deleted!
|
--resistAttr | <attrName> | If the damageType(s) string is included in the current value of the denoted attribute, then the --resistanceRule will be applied | npc_resistances | --resistAttr|npc_resistances --resistAttr|npc_resistances, npcd_resistances |
If multiple attribute names are supplied, the script will check both NOTE: default is npc_resistances if this subcommand is omitted |
--vulnerableAttr | <attrName> | If the damageType(s) string is included in the current value of the denoted attribute, then the --vulnerableRule will be applied | npc_vulnerabilities | --vulnerableAttr|npc_vulnerabilities --vulnerableAttr|npc_vulnerabilities, someOtherAttributeName |
If multiple attribute names are supplied, the script will check both NOTE: default is npc_vulnerabilities if this subcommand is omitted |
--immunityAttr | <attrName> | If the damageType(s) string is included in the current value of the denoted attribute, then the --immunityRule will be applied | npc_immunities | --immunityAttr|npc_immunities --immunityAttr|npc_immunities, someOtherAttributeName |
If multiple attribute names are supplied, the script will check both NOTE: default is npc_immunities if this subcommand is omitted |
--resistanceRule | <expression> | The math operation (accepts single Operator and Operand) to be applied if target is resistant to damageType(s) | *0.5 | --resistanceRule|*0.5 --resistanceRule|/2 --resistanceRule|-10 |
NOTE: default is "*0.5" if this subcommand is omitted |
--vulnerableRule | <expression> | The math operation (accepts single Operator and Operand) to be applied if target is vulnerable to damageType(s) | *2 | --vulnerableRule|*2 --vulnerableRule|+10 |
NOTE: default is "*2" if this subcommand is omitted |
--immunityRule | <expression> | The math operation (accepts single Operator and Operand) to be applied if target is immune to damageType(s) | *0 | --immunityRule|*0 | NOTE: default is *0 if this subcommand is omitted |
Chat Output Style Commands
Command | Parameter(s) | Description | Default Value | Example(s) | Additional Notes |
---|---|---|---|---|---|
--title | <text> | the title of the chat output table | SmartAoE | --title|Burning Hands | NOTE: all chat output options will revert to default values if omitted from the macro |
--leftsub | <text> | subtitle of the chat output table. If rightsub is included, they will be separated by a diamond icon | --- | --leftsub|some text --leftsub|Slot level ?{Cast at what level?|3,3|4,4|5,5|6,6|7,7|8,8|9,9} |
" " |
--rightsub | <text> | subtitle of the chat output table. If leftsub is included, they will be separated by a diamond icon | --- | --rightsub|some text --rightsub|DC @{selected|spell_save_dc} DEX |
" " |
--desc | <text> | optional description at bottom of chat output table | --- | --desc| Line 1 of the description%br%Line 2 of the description | NOTE: If you want to add line breaks in your description, use the code %br% as shown in the example to the left |
--hideNames | <true/yes/1/false/no/0> | If true, the token names will be replaced with Target_# | FALSE | --hidenames|1 | " " |
--titlecardbackground | <html> | Value to set the html "background-image:" property of the title field of the chat output | linear-gradient(red, yellow) | --titlecardbackground|linear-gradient(#995522, blue) --titlecardbackground|url('https://s3.amazonaws.com/files.d20.io/images/ 248538196/c9xft1A2jWNqOkBsxQwyLQ/max.jpg?1633278017');background-size: 100% 100%; background-repeat: no-repeat; |
" " |
--titlefontface | <fontName> | Font style of the chat output title | Contrail One | --titlefontface|Contrail One | " " |
--titlefontcolor | <#RRGGBB> | Font color of the chat output title | #EEEEEE | --titlefontcolor|#EEEEEE --titlefontcolor|red |
" " |
--titlefontsize | <html size> | Font size of the chat output title | 1.2em | --titlefontsize|1.2em --titlefontsize|18px |
" " |
--titlefontlineheight | <html size> | line-height of the chat output title | 1.2em | --titlefontlineheight|1.2em --titlefontlineheight|18px |
" " |
--titlecardbottomborder | <html> | Html border of the table title | 2px solid #444444; | --titlecardbottomborder|2px solid #444444 | " " |
--subtitlefontface | <fontName> | Font style of the chat output subtitle | Tahoma | --subtitlefontface|Tahoma | " " |
--subtitlefontcolor | <#RRGGBB> | Font color of the chat output subtitle | #333333 | --subtitlefontcolor|#333333 | " " |
--subtitlefontsize | <html size> | Font size of the chat output subtitle | 13px | --subtitlefontsize|13px | " " |
--bodyfontface | <fontName> | Font style of the chat output body | Helvetica | --bodyfontface|Helvetica | " " |
--bodyfontsize | <html size> | Font size of the chat output body | 14px | --bodyfontsize|14px | " " |
--tablebgcolor | <#RRGGBB> | Background color of the damage and description fields | #EEEEEE | --tablebgcolor|#EEEEEE | " " |
--tableborder | <html> | Html border of the table | 2px solid #000000; | --tableborder|2px solid #000000; | " " |
--tableborderradius | <html> | Html border radius of the table | 6px; | --tableborderradius|6px; | " " |
--tableshadow | <html> | Html shadow of the table | 5px 3px 3px 0px #aaa; | --tableshadow|5px 3px 3px 0px #aaa; | " " |
--evenrowbackground | <#RRGGBB> | Background color of the even rows of the Save fields | #DDDDDD | --evenrowbackground|#DDDDDD | " " |
--oddrowbackground | <#RRGGBB> | Background color of the odd rows of the Save fields | #FFFFFF | --oddrowbackground|#FFFFFF | " " |
--evenrowfontcolor | <#RRGGBB> | Font color of the even rows of the Save fields | #000000 | --evenrowfontcolor|#000000 | " " |
--oddrowfontcolor | <#RRGGBB> | Font color of the odd rows of the Save fields | #000000 | --oddrowfontcolor|#000000 | " " |
Chat Output Cheat Sheet
The following describes the various regions and features of the chat output, along with labels for which #Chat Output Style Commands are relevant to which regions
NOTE: if a target token does not have artwork, the icon region will be blank, but the ping-pull will still function if that region is clicked. Ping-pulls are visible to all players.
NOTE: Results for tokens on the GM layer will be whispered to the GM. Ping-pulls from from this area will only be visible to the GM.
Quirks, Recommendations, Known Issues and/or TBD
- (Quirk) If you specify a fixed radius for a square or circle AoE, the actual area covered will depend on whether the "float" keyword is used in the
--aoeType|
command.- If "float" keyword is included (AoE untethered from sourceToken), you will get a true radius. E.g. a 10ft radius covers a 4x4 square
- If "float" keyword is omitted (AoE is tethered to sourceToken), the origin token square is not counted toward the radius. E.g. a 10ft radius will be "within 10ft of the token", making the actual AoE cover a 5x5 square region. However, see issue #1 below
- (Quirk) For float AoE types, if
--forceIntersection
is omitted, the default behavior is to force the controlToken to intersection, unless the AoE size is one square or less. For larger AoEs you typically want to center the AoE on an intersection, while for single square AoEs you typically want it to be centered in the square via snap to center. If--forceIntersection
is explicitly set, the script will honor the explicit setting. - (Quirk) When applying condition markers from a custom uploaded marker set, the full marker name::number must be included in the --conditionFail command as shown below. The easiest way to find the name::number code for a custom set is to open your token-mod handout and scroll to the section with all the status markers as they will be listed there. If you use one of the default Roll20 markers, you would only need the name. You can mouse over the radial menu in the VTT to find just the name.
- (Recommendation) For 5e-style cones, I recommend starting with
--minGridArea|0.25
and--origin|nearest, face
. Requiring more area coverage may start to omit the squares nearest the sourceToken for some orientations. Allowing the cone to originate on token faces gives more flexibility in positioning. - (Recommendation) For PF-style cones, I recommend starting with
--minGridArea|0.5
,--origin|nearest
, and--forceIntersection|1
to better approximate the rules. However, see issue #4 below.
- (Issue, TBD) Square and circle AoE's of fixed radius, "nearest" origin pt, and linked to the sourceToken (i.e. not using the "float" keyword) does not properly take into account the token size, so currently only accurate for a 1 square token. This is on the list of things to fix.
- (Issue, TBD) If 5e Proficency Dice are used, there is currently no way to reference the rolled DC and display in the Subtitle region. Prob going to make a Macro-level variable to allow a replacement of that text with the rolled value. Either way, the DC will need to be wrapped in square brackets to perform the roll, e.g.
--dc|[[@{selected|spell_save_dc}]]
(Issue, TBD) If you click on the token icon in the chat output, a ping-pull is performed for all players. Saving throw results for tokens on the GM layer are whispered to the GM. However, currently if the GM clicks the token icon in chat, it will still ping-pull everyone, potentially revealing the token's location to players. This is on the list of things to fix.(fixed!)(Issue, no fix?) Existing Pathfinder cone templates do not appear to use a uniform rule for determining if a square is in the cone. SmartAoE applies the same(fixed with new PFcone and PFcircle aoeTypes!)--minGridArea
percentage rule to each grid square. This is more mathematically "fair", IMO, but those wishing to adhere strictly to PF cone template guidelines will likely need to use another method than SmartAoE (unless someone comes up with a combination of settings that work within the SmartAoE framework)- (Issue, TBD) Resistance/immunity rules are simple at present. If a creatures is resistant to, say, piercing damage from only non-silvered or non-magical sources, the script currently does not account for this. Each game ruleset and naming convention is different, and I haven't yet come up with an elegant way to handle this that is generic enough for multiple rulesets.
- (Roll20 Issue) If you make a very large AoE and trigger fx, you may encounter some lag depending on ...??? This seems to be a Roll20 quirk that even affects single instances of fx. Just be aware of this.
Using custom images for title background
The --titlecardbackground
subcommand will accept css styling information to further personalize the chat output. One fun trick is to utilize an image for your title background.
Example:
--titlecardbackground|url('https://s3.amazonaws.com/files.d20.io/images/
248538196/c9xft1A2jWNqOkBsxQwyLQ/max.jpg?1633278017');background-size: 100% 100%; background-repeat: no-repeat;
If you have something in your image library that you want to use, TheAaron came up with a simple script to give you the URL for that image. Install the following, drag your art to a map, and type !GetTokenUrl
. The URL will be printed to chat!
(() => { var CMD = '!GetTokenUrl'; on('chat:message', msg => { if(msg.content === CMD && msg.selected.length > 0) { var urls = _.map(msg.selected, selected => { var token = getObj('graphic', selected._id); return token.get('imgsrc'); }); sendChat('GET TOKEN URLs', urls.join('<br/><br/>')); } }); })();
Example SmartAoE Macros
Simple 20ft cube
This macro has bare-bones functionality. It just spawns a 20ft cube, using the float keyword to disconnect it from the sourceToken. Note that since the "float" keyword was used, the script automatically snaps the controlToken to the grid intersections.
!smartaoe {{ --aoeType|square, float --radius|10ft }}
Simple variable cube
This macro has bare-bones functionality. It just creates a square AoE linked to the sourceToken and creates burn-fire fx on trigger. Radius is variable based on relative positions of source & control tokens.
!smartaoe {{ --aoeType|square --fx|burn-fire }}
Simple 5e fixed-radius cone with origin rotation
Creates a simple 5e-style 30ft cone with default coloration (no trigger effects). The origin point of the cone is rotated counter-clockwise and then clockwise around the token to fine tune the cone positioning using !smartrotateorigin ccw/cw
commands in separate ability macros (described in #Initial Setup/Install. Both the token corners and faces are valid origin points due to the command --origin|nearest, face
in the AoE-generation macro.
!smartaoe {{ --aoeType|5econe --radius|30ft --minGridArea|0.25 --origin|nearest, face }}
!smartrotateorigin ccw
!smartrotateorigin cw
True radius 15ft cone approximation
The following uses the "cone" aoeType, with a query determining the cone radius. Radius is fixed at 15ft.
!smartaoe {{ --aoeType|cone, ?{Enter cone angle|90} --radius|15ft --minGridArea|0.50 --origin|nearest, face }
Pathfinder 20ft circle
The "PFcircle" aoeType performs an additional filter to the affected grid squares, using the "every other diagonal counts as 2 squares" rule.
!smartaoe {{ --aoeType|PFcircle, float --radius|20ft --minGridArea|0.50 }}
Burning Hands (5e)
This macro creates a 15ft 5e-style cone using the default AoEControlToken character for the controlToken, allows token faces to be the point of origin, triggers fire fx, rolls 5e DEX saves (ignoring tokens with a "1" in a custom attribute called "SmartAOE_ignore", and auto applies damage to the tokens after saves are made. Also, green/red condition markers for pass/fail are applied, and tokens having 0 in their bar1 field will gain the "dead" condition marker. NOTE: The macro below includes --instant|1
, but the animated gif was created before that command was available.
!smartaoe {{ --title|Burning Hands --leftsub|Slot level ?{Cast at what level?|1,1|2,2|3,3|4,4|5,5|6,6|7,7|8,8|9,9} --rightsub|DC @{selected|spell_save_dc} DEX --aoeType|5econe --forceIntersection|0 --radius|15ft --origin|nearest, face --minGridArea|0.25 --minTokArea|0.25 --fx|burn-fire --dc|@{selected|spell_save_dc} --saveFormula|5eDEX --damageFormula1|[[(?{Cast at what level?}+2)d6]] --damageType1|Fire --ignore|SmartAOE_ignore,1 --instant|1 --autoApply|1 --bar|1 --conditionFail|red --conditionPass|green --zeroHPmarker|dead --desc|As you hold your hands with thumbs touching and fingers spread, a thin sheet of flames shoots forth from your outstretched fingertips. Each creature in a 15-foot cone must make a Dexterity saving throw. A creature takes 3d6 fire damage on a failed save, or half as much damage on a successful one.%br%%br%The fire ignites any flammable objects in the area that aren't being worn or carried. }}
Faerie Fire (5e)
This macro creates a 20ft cube (square, float). On trigger, creates fx and autorolls saving throws. On a failed save, a condition marker is applied to the token to indicate that it now grants advantage. Since this particular condition marker is from a custom uploaded marker set, the full marker name::number
must be included in the --conditionFail
command as shown below. If you use one of the default Roll20 markers, you would only need the name. This is a quirk of the Roll20 api. Chat output is customized for a more Fae-appropriate theme. NOTE: The macro below includes --instant|1
, but the animated gif was created before that command was available.
!smartaoe {{ --title|Faerie Fire --leftsub|Slot level 1 --rightsub|DC @{selected|spell_save_dc} DEX --titlecardbackground|linear-gradient(#af70c2, #e0cce6) --oddrowbackground|#af70c2 --evenrowbackground|#e0cce6 --oddrowfontcolor|#000000 --tablebgcolor|#e0cce6 --aoeColor|#bd39e650 --aoeOutlineColor|#9611bf --aoeType|square, float --radius|10ft --minTokArea|0.25 --fx|bubbling-magic --dc|@{selected|spell_save_dc} --saveFormula|5eDEX --autoApply|1 --instant|1 --conditionFail|GrantsAdvantage::1510122 --desc|Each object in a 20-foot cube within range is outlined in blue, green, or violet light (your choice). Any creature in the area when the spell is cast is also outlined in light if it fails a Dexterity saving throw. For the duration, objects and affected creatures shed dim light in a 10-foot radius.%br%%br%Any attack roll against an affected creature or object has advantage if the attacker can see it, and the affected creature or object can't benefit from being invisible. }}
Ice Storm (5e)
This macro creates a 20ft radius "floating" circle using a custom multi-sided token from a character called "GenericSpellAoE" (default token set up beforehand) for the controlToken, triggers ice fx, rolls 5e DEX saves (ignoring tokens with a "1" in a custom attribute called "SmartAOE_ignore", and delays damage application to the tokens until the P/F icons are clicked in the chat output. There are two damage types for this spell. Also, tokens having 0 in their bar1 field after damage is applied from this click event will gain the "dead" condition marker. There are also several changes to the formatting of the chat output from the default. NOTE: The macro below includes--instant|1
, but the animated gif was created before that command was available.
!smartaoe {{ --title|Ice Storm --leftsub|Slot level ?{Cast at what level?|4,4|5,5|6,6|7,7|8,8|9,9} --rightsub|DC @{selected|spell_save_dc} DEX --titlecardbackground|linear-gradient(blue, cyan) --oddrowbackground|#00ccff --evenrowbackground|#edfcfc --oddrowfontcolor|#000000 --tablebgcolor|#edfcfc --controlTokName|GenericSpellAoE --controlTokSize|8 --controlTokSide|14 --aoeColor|#00ccff50 --aoeOutlineColor|#0099ff --aoeType|circle, float --radius|20ft --minGridArea|0.25 --minTokArea|0.25 --fx|burn-frost --dc|@{selected|spell_save_dc} --saveFormula|5eDEX --damageFormula1|[[(?{Cast at what level?}-2)d8]] --damageType1|Bludgeon --damageFormula2|[[4d6]] --damageType2|Cold --ignore|SmartAoE_Ignore,1 --instant|1 --autoApply|0 --bar|1 --zeroHPmarker|dead --desc|A hail of rock-hard ice pounds to the ground in a 20-foot-radius, 40-foot-high cylinder centered on a point within range. Each creature in the cylinder must make a Dexterity saving throw. A creature takes 2d8 bludgeoning damage and 4d6 cold damage on a failed save, or half as much damage on a successful one.%br%%br%Hailstones turn the storm's area of effect into difficult terrain until the end of your next turn. }}
Spirit Guardians (5e)
A couple of different concepts in this macro. First, since the spell is centered on and moves with the caster, the command --controlTokName|self
is used. Since the default behavior for a "float" type AOE is to force the origin to a grid intersection and we want the token to snap to grid, we use --forceIntersection|0
. Technically, 5e rules count diagonal squares as only 5ft. I used a circle here with true diagonal distance and requiring 50% of the grid to be covered, but you could easily change to a square by changing the aoeType command to --aoeType|square, float
. Since the spell effects are triggered at the start of the creature's turn, we don't want to trigger the entire AoE region. Instead, we will pass a target token_id to an alternate triggering command (!smartquery @{target|Choose a Target|token_id}
). When the spell ends, it is removed with another alternate command (!smartremove
). Both of these commands have been added as an ability on the caster token.
!smartaoe {{ --title|Spirit Guardians --leftsub|Slot level ?{Cast at what level?|3,3|4,4|5,5|6,6|7,7|8,8|9,9} --rightsub|DC @{selected|spell_save_dc} WIS --titlecardbackground|url('https://s3.amazonaws.com/files.d20.io/images/253253846/-01wiItwF1nthkh7UwRkIQ/max.png?1635696123');background-size: 100% 100%; background-repeat: no-repeat --oddrowbackground|#ffffaa --evenrowbackground|#ffffff --oddrowfontcolor|#000000 --tablebgcolor|#ffffff --aoeColor|#ffff0050 --aoeOutlineColor|#cccc00 --gridColor|#ffffff00 --controlTokName|self --aoeType|circle, float --radius|15ft --forceIntersection|0 --minGridArea|0.5 --minTokArea|0.25 --fx|burn-holy --dc|@{selected|spell_save_dc} --saveFormula|5eWIS --damageFormula1|[[?{Cast at what level?}d8]] --damageType1|Radiant --ignore|SmartAoE_Ignore,1 --autoApply|1 --bar|1 --zeroHPmarker|dead }}
Aura of Vitality (5e Healing)
This macro is nearly identical to the Spirit Guardians macro above (read description for explanations). The only differences are: 1)the --noSave|1
command is used as there is no saving throw (the save becomes hardcoded to 0 and the DC hardcoded to 99999), 2)radius is larger, 3)the damageFormula results in a negative number! (this is interpreted as healing by the script), 4)no tokens are ignored, and 5)the title background image URL is different.
The macro creates a 30ft radius "floating" circle centered on caster, has transparent AoE gridlines, triggers holy fx, forces targets to "fail" the saving throw, and automatically "heals" (applies negative damage to) the token. Tokens are not healed over their max.
!smartaoe {{ --title|Aura of Vitality --leftsub|Slot level 3 --rightsub|No Save --titlecardbackground|url('https://s3.amazonaws.com/files.d20.io/images/253287692/0dpMLj12wwv9cRy5RRw6LA/max.png?1635706258');background-size: 100% 100%; background-repeat: no-repeat --oddrowbackground|#ffffaa --evenrowbackground|#ffffff --oddrowfontcolor|#000000 --tablebgcolor|#ffffff --aoeColor|#ffff0050 --aoeOutlineColor|#cccc00 --gridColor|#ffffff00 --controlTokName|self --aoeType|circle, float --radius|30ft --forceIntersection|0 --minGridArea|0.5 --minTokArea|0.25 --fx|bubbling-holy --noSave|1 --damageFormula1|[[-1*2d6]] --damageType1|(Healing) --autoApply|1 }}
Custom Ruleset Example, 180deg cone
The following example shows how to implement a custom ruleset via deferral characters in the--saveFormula
command. In our fictional game system, successes are determined by rolling 2d100kh1, adding modifiers, and comparing to a static value of 66. In this example, modifiers are target defense (@{psi_shield_bonus}) and "caster" power level (@{selected|psi_strength}. Note that the "roll" is deferred to script runtime by replacing inline roll brackets [[...]] with the <<...>> constructor. Target-based attributes must be deferred as well in order for the script to use each target's attribute value. The @{...} syntax for target attributes are replaced with the a{...} constructor. Since the "caster"-based modifier is the same for all targets, the normal @{selected|attrName} syntax is used and will be expanded normally by the Roll20 parser prior to passing it over to the script. If the resulting roll is >= 66, then the defender succeeds. On a success, no damage is applied (due to the command --damageSaveRule|*0
. On a failure, damage is applied automatically, and the death-zone marker is applied. If the target has a bar1 value of 0 after damage application, the dead condition marker is also applied. This example also features a custom title background image, custom AoE colors, and a radius based on a query that is re-used between the --radius
and --rightsub
commands.
!smartaoe {{ --title|Psi Blast --leftsub|1x/day --rightsub|?{Enter radius|15ft,15ft|30ft,30ft} --subtitlefontcolor|#ffffff --titlecardbackground|url('https://s3.amazonaws.com/files.d20.io/images/254392553/tR0yBgEO-OWdvmskzHBZrg/max.png?1636291858');background-size: 100% 100%; background-repeat: no-repeat --aoeType|cone, 180 --radius|?{Enter radius} --minGridArea|0.50 --origin|nearest --forceIntersection|1 --aoeColor|#ff00ff50 --gridColor|#aa00aa --aoeOutlineColor|#880088 --fx|burn-charm --instant|1 --dc|66 --damageFormula1|[[2d10 + @{selected|psi_strength}]] --saveFormula|<<2d100kh1 + a{psi_shield_bonus} - @{selected|psi_strength}>> --damageSaveRule|*0 --conditionFail|death-zone --zeroHpMarker|dead --autoApply|1 }}
[For script writers] Registering your token change events to SmartAoE
Script writers can now register their token change events to SmartAoE!
For example, the Aura/Tint HealthColor(Forum) script looks for changes in token bar values to set the aura color. However, changes made by the api don't normally trigger this event, so it would remain idle when SmartAoE removed/added hp. By registering your token change event handler function, SmartAoE will notify your script to trigger your function after it changes any token properties.
Add something like this line to your script where you would normally register your events:
if('undefined' !== typeof SmartAoE && SmartAoE.ObserveTokenChange){ SmartAoE.ObserveTokenChange(function(obj,prev){ myEventHandlerFunction(obj, prev); }); };
So, extending the example of Aura/Tint HealthColor(Forum), the new registerEventHandlers function in that script would read as follows:
//REGISTER TRIGGERS------------ registerEventHandlers = function () { on('chat:message', handleInput); on("change:token", handleToken); on('add:token', function (t) { _.delay(() => { let token = getObj('graphic', t.id), prev = JSON.parse(JSON.stringify(token)); handleToken(token, prev, "YES"); }, 400); }); //register this script to SmartAoE to handle linked bar hp changes if('undefined' !== typeof SmartAoE && SmartAoE.ObserveTokenChange){ SmartAoE.ObserveTokenChange(function(obj,prev){ handleToken(obj, prev, "NO"); }); }; };
This should look familiar to those that have done the same thing with Token-mod, as I blatantly ripped off Aaron's code. I apologize for nothing ;)