Character Vault
Any Concept / Any System
Compendium
Your System Come To Life
Roll20 for Android
Streamlined for your Tablet
Roll20 for iPad
Streamlined for your Tablet

Personal tools

Script:SmartAoE

From Roll20 Wiki

Jump to: navigation, search

Main Page: API:Script Index

API ScriptAuthor: David M
Code: SmartAoE
Dependencies: GitHubLogo.png
Conflicts: None

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.

Example of multiple AoE types, plus a trigger with fx, saving throws, and chat output
SmartAoE Nick Olivo (Dec. 2021)

Contents


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

  1. Install the SmartAoE api script, currently found on GitHub here: v0.23
  2. Install the GitHubLogo.png API script (available as 1-click install). SmartAoe is dependent on this utility script!
  3. 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!
    1. 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 named AoEControlToken. You can use a Multi-Sided Token if you'd like, for which you may set the side and size via commands when spawned.
    2. 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.
  4. 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.
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
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


AoEGenerator collections macro output.png

Acknowledgements

  • Folks I've shamelessly stolen and/or modified code from:
  • 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
Support me on Patreon
I've been asked by a few folks if they can provide support in appreciation for a script or other help. *If you find yourself falling into that group, too, then thank you! If not, no worries - it's not why I do this.* I just want everyone to have as much fun as possible playing the games that they love!
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", "wall", "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.
--width <###><optional units like ft. or "u"> the width of a wall-type AoE in Pixels or units 1u --width|3u
--width|10ft
only used for "wall" AoE's
--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 intersections
NOTE: 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:
  • "[[…]]" is replaced by "<<…>>"
  • "@{…}" is replaced by "a{…}"

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 <#, optional #, optional #> which token bubble(s) to apply damage. Default = 1 1 --bar|1
--bar|3,1
--bar|3,1,2
If multiple bar values are defined:
*For damage, once the first bar value is reduced to 0, any excess damage will be applied to the subsequent bar number until all damage is accounted for or all denoted bar values are 0.
*For healing (negative damage), bars will fill up until the max value is reached, then excess will be applied to the next bar value, etc. If the bar value has no max, there will be no limit to the amount of "healing" done.
--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;
see #Using custom images for title background for more info on using custom title background images
--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.

SmartAoE Chat Output - Cheat Sheet.jpg

Quirks, Recommendations, Known Issues and/or TBD

  1. (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.
    1. If "float" keyword is included (AoE untethered from sourceToken), you will get a true radius. E.g. a 10ft radius covers a 4x4 square
    2. 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
  2. (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.
  3. (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.

  1. (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.
  2. (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.

  1. (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.
  2. (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}]]
  3. (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!)
  4. (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 --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) (fixed with new PFcone and PFcircle aoeTypes!)
  5. (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.
  6. (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;

SmartAoE example - custom image background - Lightning Bolt.jpg
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

I have put several 5e spell macros in a GitHub repo here:[Spell Repo]

Simple 20ft cube

SmartAoE Simple 20ft Cube.gif

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

SmartAoE Simple Variable Cube.gif

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

SmartAoE Cone Origin Rotation (optimized).gif

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

SmartAoE true radius cone example.gif

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

SmartAoE Pathfinder 40ft circle example.gif

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 Burning Hands 1200x650.gif
!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 Faerie Fire 1200x650.gif
!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 Ice Storm 922x499.gif
!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.

The macro creates a 15ft radius "floating" circle centered on caster, has transparent AoE gridlines, triggers holy fx, rolls 5e WIS saves for affected tokens (ignoring tokens with a "1" in a custom attribute called "SmartAOE_ignore"), and automatically applies damage to the token. Tokens having 0 in their bar1 field after damage is applied will gain the "dead" condition marker. The chat output has been customized, including the use of a background image in the title. This image has been uploaded into my personal art library and is referenced by its URL.
SmartAoE Spirit Guardians Example.gif
!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.

Note: Due to a quirk of the Roll20 inline roll parser, you can't have a negative number in the first position. That is why you will notice the strange [[0 + [[0 - 2]]d6]] syntax for the healing "damage"

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.

Now, I realize that a simpler approach using token auras and a vanilla macro would work just as well. My point in including this example is to help you understand some of the syntax and behavior of the script ;)
SmartAoE Aura of Vitality Example.gif
!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|[[0 + [[0 - 2]]d6]]
  --damageType1|(Healing)
  --autoApply|1
}}

Wall of Fire (5e)

SmartAoE Wall of Fire (5e) Example.gif

The following uses the "wall" aoeType and a width of 3u (15ft on a standard map). The spell technically produces a wall 1ft wide, but all creatures in a wall square or within 10ft of that square on one side are affected, making the width effectively 15ft.

!smartaoe {{
  --title|Wall of Fire
  --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

  --aoeType|wall
  --offset|1,0
  --minGridArea|0.5
  --radius|30ft
  --width|3u
  --fx|burn-fire
  --dc|@{selected|spell_save_dc}
  --aoeColor|player
  --aoeOutlineColor|#990000
  --damageFormula1|[[(1+?{Cast at what level?})d8]]
  --saveFormula|5eDex

  --autoApply|1
  --bar|1
  --zeroHPmarker|dead
}}


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 Custom Rules Example.gif
!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 TokenMod, as I blatantly ripped off Aaron's code. I apologize for nothing ;)