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

Difference between revisions of "Script:SmartAoE"

From Roll20 Wiki

Jump to: navigation, search
m
(AoE Behavior Commands)
 
(94 intermediate revisions by 3 users not shown)
Line 1: Line 1:
= This page is a work in progress! Keep checking back for updates... =
+
{{revdate}}
== Concept ==
+
{{main|API:Script Index}}
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).
+
  
[[File:SmartAoE multi area example 1152x624.gif|900px|||Example of multiple AoE types, plus a trigger with fx, saving throws, and chat output]]
+
{{script overview
 +
|name=SmartAoE
 +
|author=[https://app.roll20.net/users/3987469/david-m David M]
 +
|dependencies={{api-gh|libInline libInline}}}}
 +
= 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).
 +
 
 +
* [https://app.roll20.net/forum/permalink/10485883/ SmartAoE forum thread]
  
 
'''''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 (<code>--autoApply|true</code>), and outputting results to chat.'''''
 
'''''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 (<code>--autoApply|true</code>), and outputting results to chat.'''''
 +
<div style="max-width:80%;float:right;">[[File:SmartAoE multi area example 1152x624.gif|800px|right|Example of multiple AoE types, plus a trigger with fx, saving throws, and chat output|class=resize]]</div>
 +
{{#evp:youtube|YzKu_rSp4vs|SmartAoE Nick Olivo (Dec. 2021)|right|750}}
 +
__TOC__
  
== How does it work? ==
+
= 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 <code>--minGridArea|0.25</code> 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 <code>--origin|nearest</code> command to automatically shift the origin point to the nearest corner or face of the origin token. There are additional commands to rotate the origin point CW/CCW and to "trigger" the effect.  
+
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 <code>--minGridArea|0.25</code> 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 <code>--origin|nearest</code> or <code>--origin|nearest, face</code> 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.
 
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), and auto application of damage. These parameters and more are described below under [[#List of Subcommands]].
+
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.  
 
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 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 <code>--hideNames|1</code> to keep your chat spoiler-free :)
+
:*''What if I want to hide the names of tokens to avoid?'' There is an option <code>--hideNames|1</code> to keep your chat spoiler-free :)
  
== Initial Setup/Install ==
+
= Initial Setup/Install =
# Install the SmartAoE api script, currently found on GitHub here: {{repo|djmoorehead/SmartAoE/tree/main/v0.14 v0.14}}
+
# Install the SmartAoE api script, currently available for one-click install or found on GitHub here: {{repo|djmoorehead/SmartAoE/tree/main/v0.28 v0.28}}
 
# Install the {{api-gh|libInline libInline}} API script (available as 1-click install). SmartAoe is dependent on this utility script!
 
# Install the {{api-gh|libInline libInline}} API script (available as 1-click install). SmartAoe is dependent on this utility script!
# Create a [[CS|character sheet]] with the default token image you want for the AoE control token. If you do not specify a <code>--controlTokName</code>, then the script will look for the default token for a character named <code>AoEControlToken</code>. You can use a [[Multi-Sided Token]], for which you may set the side and size via commands when spawned.
+
# <b>Note:</b> 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 macro utilizing the syntax described below. There are currently three different base commands:
+
## Create a [[CS|character sheet]] with the default token image you want for the AoE control token. If you do not specify a <code>--controlTokName</code>, then the script will look for the default token for a character named <code>AoEControlToken</code>. 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 <u>under</u> 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 macro(s) utilizing the syntax described below. There are currently six different base commands, though the first and third will be the most common:
  
 
::{| class="wikitable"
 
::{| class="wikitable"
 
|-
 
|-
! Syntax !! Description
+
! style="width:16em" | Syntax !! Description
 
|-
 
|-
| <nowiki>!smartaoe <subcommands></nowiki> || this is the primary api syntax. Descriptions of commands is below, but they all follow the form <code><nowiki>--commandName|parameter(s)</nowiki></code> ||  
+
| <nowiki>!smartaoe <subcommands></nowiki> || this is the primary api syntax. Descriptions of the subcommands are found here ([[#List of Subcommands]]), but they all follow the form <code><nowiki>--commandName|parameter(s)</nowiki></code> ||  
 
|-
 
|-
 
|<nowiki>!smartrotateorigin <cw/ccw></nowiki> || valid for cones only - this rotates the origin point cw or ccw along corners or faces of the origin token
 
|<nowiki>!smartrotateorigin <cw/ccw></nowiki> || valid for cones only - this rotates the origin point cw or ccw along corners or faces of the origin token
 
|-
 
|-
|<nowiki>!smarttrigger</nowiki> || currently no subcommands. Triggers the fx and does all the stuffs
+
|<nowiki>!smarttrigger</nowiki> || 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).
 
|-
 
|-
 +
|<nowiki>!smartquery <optional token_id></nowiki> || This checks a <b>selected token</b> (if no token_id parameter supplied) or a <b>targeted token</b> (requires a token_id parameter) for overlapping AoEs and triggers any/all AoE's <b> that are active in the target token's square only</b>. Useful for effects that occur at the start or end of a creature's turn (e.g. 5e Stinking Cloud or Flaming Sphere)
 +
|-
 +
|<nowiki>!smartremove</nowiki> || 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 <code>--controlTokName<nowiki>|</nowiki>self</code> in the [[#List of Subcommands]] below for more details
 +
|-
 +
|<nowiki>!smartclearcache</nowiki> || currently no subcommands. All of the AoE links in the <b>Campaign</b> 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.
 
|}
 
|}
  
{{notebox|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.}}
+
{{notebox|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.<br><b>Note:</b> 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!}}
::::::{| class="wikitable"
+
::::::::{| class="wikitable"
 
|-
 
|-
! Ability Name !! Ability Content
+
! Ability Name !! Ability Content || Description
 
|-
 
|-
| CCW || <nowiki>!smartrotateorigin ccw</nowiki>
+
| CCW || <nowiki>!smartrotateorigin ccw</nowiki> || rotates a cone origin point counter-clockwise
 
|-
 
|-
| CW || <nowiki>!smartrotateorigin cw</nowiki>
+
| CW || <nowiki>!smartrotateorigin cw</nowiki> || rotates a cone origin point clockwise
 
|-
 
|-
| Trigger || <nowiki>!smarttrigger</nowiki>
+
| Trigger || <nowiki>!smarttrigger</nowiki> || triggers the AoE effects in <b>every</b> affected grid square
 
|-
 
|-
 
|}
 
|}
  
== Acknowledgements ==
+
{{notebox|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}}
*Folks I've shamelessly stolen code from:
+
::::::::{| class="wikitable"
 +
|-
 +
! Ability Name !! Ability Content || Description
 +
|-
 +
| aTrigger-ALL || <nowiki>!smarttrigger</nowiki> || triggers the AoE effects in <b>every</b> affected grid square
 +
|-
 +
| aTrigger-Target || <nowiki>!smartquery @{target|Choose a Target|token_id}</nowiki> || prompts for a target token and triggers the AoE effects <b>only on that token</b>
 +
|-
 +
| aRemove || <nowiki>!smartremove</nowiki> || removes all AoE tokens and grid shading linked to the selected caster token
 +
|-
 +
|}
 +
{{notebox|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).}}
 +
 
 +
::::::::[[File:AoEGenerator collections macro output.png|150px]]
 +
 
 +
= Acknowledgements =
 +
*Folks I've shamelessly stolen and/or modified code from:
 
**[https://app.roll20.net/users/104025/the-aaron TheAaron] (numerous sources)
 
**[https://app.roll20.net/users/104025/the-aaron TheAaron] (numerous sources)
 
**[https://app.roll20.net/users/2365448/kurt-j Kurt J.] (chat styling, Bresenham line algorithm [[Scriptcards]])
 
**[https://app.roll20.net/users/2365448/kurt-j Kurt J.] (chat styling, Bresenham line algorithm [[Scriptcards]])
Line 56: Line 88:
 
**[https://app.roll20.net/users/162065/keithcurtis keithcurtis](ping-pull and api triggers from from chat icons {{forum|post/10381135/script-reporter-1-dot-x Reporter}})
 
**[https://app.roll20.net/users/162065/keithcurtis keithcurtis](ping-pull and api triggers from from chat icons {{forum|post/10381135/script-reporter-1-dot-x Reporter}})
 
*All-around great guy whose brain I've picked too many times to count: [https://app.roll20.net/users/5962076/timmaugh timmaugh]
 
*All-around great guy whose brain I've picked too many times to count: [https://app.roll20.net/users/5962076/timmaugh timmaugh]
*My Patreon supporters for early beta testing and feedback
+
*My home game players and my Patreon supporters for beta testing and feedback, with special thanks to [https://app.roll20.net/users/6983054/fr FR] and [https://app.roll20.net/users/3689319/michael-c Michael C]
  
 
<div style="width: 50%; padding: 5px 5px 5px 5px; margin-top:10px; background:#89bcdd; border-style: solid; border-width: 1px;border-radius: 5px;text-align: center;">'''[https://www.patreon.com/bePatron?u=38375296 Support me on Patreon]'''<div style="text-align: justify">'''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!'''<br>''Disclaimer: Patreon campaigns are not affiliated with Roll20. Contributions are entirely voluntary and Roll20 cannot provide support or refunds for contributions.''</div></div>
 
<div style="width: 50%; padding: 5px 5px 5px 5px; margin-top:10px; background:#89bcdd; border-style: solid; border-width: 1px;border-radius: 5px;text-align: center;">'''[https://www.patreon.com/bePatron?u=38375296 Support me on Patreon]'''<div style="text-align: justify">'''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!'''<br>''Disclaimer: Patreon campaigns are not affiliated with Roll20. Contributions are entirely voluntary and Roll20 cannot provide support or refunds for contributions.''</div></div>
  
== List of Subcommands ==
+
= List of Subcommands =
=== AoE Behavior Commands ===
+
== AoE Behavior Commands ==
 
{| class="wikitable mw-collapsible"
 
{| class="wikitable mw-collapsible"
 
|-
 
|-
Line 68: Line 100:
 
| --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 || <nowiki>--radius|60ft</nowiki><br><nowiki>--radius|12u </nowiki> || 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.
 
| --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 || <nowiki>--radius|60ft</nowiki><br><nowiki>--radius|12u </nowiki> || 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 coneWidth>,<optional "float"> || currently supports "line", "square", "circle", "cone", and "5econe" || line || <nowiki>--aoetype|5econe</nowiki><br><nowiki>--aoetype|cone, 90</nowiki><br><nowiki>--aoetype|circle, float</nowiki><br><nowiki>--aoetype|line</nowiki> || The "float" keyword "untethers" the AoE from the sourceToken, creating the AoE around the controlToken only (e.g. a Fireball spell)<br><b>NOTE</b>: default is "line" if this subcommand is omitted
+
| --aoetype || <type>,<optional coneAngle> or <optional "float"> || currently supports "line", "square", "circle", "PFcircle", "wall", "cone", "PFcone", and "5econe" || line || <nowiki>--aoetype|5econe</nowiki><br><nowiki>--aoetype|cone, 90</nowiki><br><nowiki>--aoetype|circle, float</nowiki><br><nowiki>--aoetype|line</nowiki><br><nowiki>--aoetype|PFcone</nowiki> || --The "float" keyword "untethers" the AoE from the sourceToken, creating the AoE around the controlToken only (e.g. a Fireball spell)<br>--"5econe" will create a triangular shape whose max width equals its length, whereas a "cone" will use a true radius and coneAngle<br>--The "PFxxxx" types will perform an additional filtering on affected grids using the "every other diagonal counts as 2squares" rule.<br><b>NOTE</b>: 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 || <nowiki>--width|3u</nowiki><br><nowiki>--width|10ft</nowiki> || 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 || <nowiki>--instant|1</nowiki> || <b>NOTE</b>: 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 || <nowiki>--origin|nearest</nowiki><br><nowiki>--origin|nearest, face</nowiki><br><nowiki>--origin|center</nowiki> || "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.
 
| --origin || <style> <optional "face"> || AoE starts in center or edge of square (ignored for line AoE’s) || center || <nowiki>--origin|nearest</nowiki><br><nowiki>--origin|nearest, face</nowiki><br><nowiki>--origin|center</nowiki> || "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 || <nowiki>--forceIntersection|0</nowiki> || 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. <b>NOTE</b>: This command is ignored and automatically set to true if a "float" style circle or square AoE is used
+
| --forceIntersection|| <true/yes/1/false/no/0> || forces the dragged controlToken center to the intersection of grid squares || false || <nowiki>--forceIntersection|0</nowiki> || 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. <b>NOTE</b>: 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 || <nowiki>--mingridarea|0.1</nowiki> || 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 <nowiki>--forceIntersection|1</nowiki>'''. More than 50% might start making cones not "emanate" fully from the source token
 
| --mingridarea || <#> || what percentage of the grid square’s area must lie within the AoE to be included in the final affected area || 0.01 || <nowiki>--mingridarea|0.1</nowiki> || 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 <nowiki>--forceIntersection|1</nowiki>'''. More than 50% might start making cones not "emanate" fully from the source token
Line 80: Line 116:
 
| --fx || <style>-<type> || will spawn fx in affected squares when triggered || style="text-align:center"|--- || <nowiki>--fx|burn-death</nowiki><br><nowiki>--fx|glow-holy</nowiki> || fx will spawn at each affected square
 
| --fx || <style>-<type> || will spawn fx in affected squares when triggered || style="text-align:center"|--- || <nowiki>--fx|burn-death</nowiki><br><nowiki>--fx|glow-holy</nowiki> || fx will spawn at each affected square
 
|-
 
|-
| --aoecolor || <#RRGGBBTT> || determines the fill color of the AoE’s affected squares || #ff000050 || <nowiki>--aoeColor|#ff000050</nowiki> || <b>NOTE</b>: default is #ff000050 if this subcommand is omitted
+
| --aoeColor || <#RRGGBBTT> || determines the fill color of the AoE’s affected squares || #ff000050 || <nowiki>--aoeColor|#ff000050</nowiki> || <b>NOTE</b>: default is #ff000050 if this subcommand is omitted
 
|-
 
|-
| --aoeOutlineColor || <#RRGGBB> || determines the line color of the AoE outline || #ff0000 || <nowiki>--aoeColor|#ff000050</nowiki> || <b>NOTE</b>: default is #ff0000 if this subcommand is omitted
+
| --aoeOutlineColor || <#RRGGBB> || determines the line color of the AoE outline || #ff0000 || <nowiki>--aoeOutlineColor|#ff000050</nowiki> || <b>NOTE</b>: default is #ff0000 if this subcommand is omitted
 
|-
 
|-
| --controlTokName || <charName> || The name of the character sheet whose default token will be used as the AoE control token || AoEControlToken || <nowiki>--controlTokName|GenericSpellAoE</nowiki> || 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.<br><b>NOTE</b>: default is "AoEControlToken" if this subcommand is omitted
+
| --gridcolor || <#RRGGBBTT> || determines the stroke(line) color of the AoE’s affected squares || #000000 || <nowiki>--gridColor|#ff994450</nowiki> || To have no grid lines, set transparency to max by adding "00" to digits 7&8<br><b>NOTE</b>: 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 || <nowiki>--controlTokName|Fireball</nowiki><br><nowiki>--controlTokName|self</nowiki> || 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.<br><b>NOTE</b>: "self" can only be used for square/circle aoeTypes with the float keyword and with pre-defined radius<br><b>NOTE</b>: if "self" is used, then <code>--controlTokSize</code> and <code>--controlTokSide</code> will be ignored.<br><b>NOTE</b>: if "self" is used, it is recommended to set <code>--forceIntersection<nowiki>|</nowiki>0</code> to prevent the default behavior of centering float AoE's at grid intersections<br><b>NOTE</b>: default is "AoEControlToken" if this subcommand is omitted.
 
|-
 
|-
 
| --controlTokSize || <#> || The size (in squares) of the control token || 1 || <nowiki>--controlTokSize|8</nowiki> || <b>NOTE</b>: default is 1 if this subcommand is omitted
 
| --controlTokSize || <#> || The size (in squares) of the control token || 1 || <nowiki>--controlTokSize|8</nowiki> || <b>NOTE</b>: default is 1 if this subcommand is omitted
Line 97: Line 135:
 
|-
 
|-
 
| --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. || style="text-align:center"|--- || <nowiki>--playerid|-MlBqIRAtRSwAZ-8Jr_E</nowiki> ||  
 
| --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. || style="text-align:center"|--- || <nowiki>--playerid|-MlBqIRAtRSwAZ-8Jr_E</nowiki> ||  
|-  
+
|-
 +
| --spawnSound|| <trackName> || plays the specified jukebox track when the control token is spawned || style="text-align:center"|--- || <nowiki>--spawnSound|Thundercrack</nowiki> ||
 +
|-
 +
| --moveSound|| <trackName> || plays the specified jukebox track when the control token is moved || style="text-align:center"|--- || <nowiki>--moveSound|Whoosh</nowiki> ||
 +
|-
 +
| --triggerSound|| <trackName> || plays the specified jukebox track when the control token is triggered || style="text-align:center"|--- || <nowiki>--triggerSound|Fireball</nowiki> ||
 +
|-
 +
| --deleteSound|| <trackName> || plays the specified jukebox track when the control token is removed from the map|| style="text-align:center"|--- || <nowiki>--deleteSound|Wilhelm scream</nowiki> ||
 +
|-
 +
| --tooltip|| <text> || adds a tooltip to the control token. Useful if multiple persistent effects are present on the same map || style="text-align:center"|--- || <nowiki>--tooltip|Stinking Cloud</nowiki> ||
 +
|-
 +
| --turnorder|| <name>,<optional value>,<optional formula>,<optional true/yes/1/false/no/0> || populate a custom turnorder entry when the control token is spawned, so you can more easily track durations, etc. || style="text-align:center"|--- || <nowiki>--turnorder|Stinking Cloud,0,+1</nowiki> <i><FONT COLOR="#555555">//current value 0, adds 1 every turn, default condition in effect to link entry to controlTok</FONT></i><br><nowiki>--turnorder|Stinking Cloud,0,1, true</nowiki> <i><FONT COLOR="#555555">//same as above, note the plus sign is not necessary, and this time we are explicitly linking to the controlTok</FONT></i><br><nowiki>--turnorder|Hold Person,10,-1, false</nowiki> <i><FONT COLOR="#555555">//current value 10, subtracts 1 every turn, entry not linked to controlTok</FONT></i> || You must specify a name, and then a current value, a formula, and a switch to determine if the entry is tied to the control token are optional. If tied to the control token, then the entry will be deleted when the control token is deleted. Otherwise you will have to delete the entry manually. The default behavior is to link to the control token. ||
 +
|-
 +
| --affectsCaster|| <true/yes/1/false/no/0> || allows the caster to be affected by the AoE || false || <nowiki>--affectsCaster|true</nowiki> ||<b>NOTE</b>: default is 0 if this subcommand is omitted ||
 +
|-
 +
| --casterCondition|| <statusmarker>, <optional "onFail"> || add a statusmarker to the source (usually caster) token || style="text-align:center"|--- || <nowiki>--casterCondition|stopwatch</nowiki><br><i><FONT COLOR="#555555">//adds a stopwatch marker to the caster regardless of the saving throw results</FONT></i><br><nowiki>--casterCondition|stopwatch, onFail</nowiki><br><i><FONT COLOR="#555555">//adds a stopwatch marker to the caster only if at least one target fails</FONT></i><br> || Useful for 5e concentration markers, etc. ||
 +
|-
 
|}
 
|}
  
=== AoE Math Commands ===
+
== AoE Math and/or Trigger Behavior Commands ==
 
{| class="wikitable mw-collapsible"
 
{| class="wikitable mw-collapsible"
 
|-
 
|-
Line 110: Line 164:
 
<b>NOTE</b>: for 5e games, you can use the shorthand "5eSTR", "5eDEX", "5eCON", "5eINT", "5eWIS","5eCHA"
 
<b>NOTE</b>: for 5e games, you can use the shorthand "5eSTR", "5eDEX", "5eCON", "5eINT", "5eWIS","5eCHA"
 
|-
 
|-
| --damageformula1 || <inline roll> || Formula to be used for damage1 || style="text-align:center"|--- || <nowiki>--damageformula1|[[8d6]]</nowiki><br><nowiki>--damageformula1|[[(5+?{Cast at what level? |3,3|4,4|5,5|6,6|7,7|8,8|9,9})d6]]</nowiki> ||  
+
| --damageformula1 || <inline roll> || Formula to be used for damage1 || style="text-align:center"|--- || <nowiki>--damageformula1|[[8d6]]</nowiki><br><nowiki>--damageformula1|[[(5+?{Cast at what level? |3,3|4,4|5,5|6,6|7,7|8,8|9,9})d6]]</nowiki><br><nowiki>--damageFormula1|[[-1*2d6]]</nowiki> || 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 || <nowiki>--damagesaverule|*0</nowiki><br><nowiki>--damagesaverule|-10</nowiki> || <b>NOTE</b>: default is "*0.5" if this subcommand is omitted
 
|-
 
|-
 
| --damagetype1 || <text> || the damage type for damage1 || style="text-align:center"|--- || <nowiki>--damageType1|Fire</nowiki> ||  
 
| --damagetype1 || <text> || the damage type for damage1 || style="text-align:center"|--- || <nowiki>--damageType1|Fire</nowiki> ||  
Line 118: Line 174:
 
| --damagetype2 || <text> || the damage type for damage2 || style="text-align:center"|--- || <nowiki>--damageType1|Fire</nowiki> ||  
 
| --damagetype2 || <text> || the damage type for damage2 || style="text-align:center"|--- || <nowiki>--damageType1|Fire</nowiki> ||  
 
|-
 
|-
| --bar || <#> || which token bubble to apply damage. Default = 1 || 1 || <nowiki>--bar|1</nowiki> ||  
+
| --bar || <#, optional #, optional #> || which token bubble(s) to apply damage. Default = 1 || 1 || <nowiki>--bar|1</nowiki><br><nowiki>--bar|3,1</nowiki><br><nowiki>--bar|3,1,2</nowiki> || If multiple bar values are defined:<br>*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.<br>*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 || <nowiki>--autoApply|1</nowiki> || Confirmation of the applied damage will be posted to chat. Tokens on GM layer will have whispered confirmation. <b>NOTE</b>: default is false if this subcommand is omitted.
 
| --autoapply || <true/yes/1/false/no/0> || Toggle to apply damage and condition markers automatically or after subsequent interaction with chat output. || FALSE || <nowiki>--autoApply|1</nowiki> || Confirmation of the applied damage will be posted to chat. Tokens on GM layer will have whispered confirmation. <b>NOTE</b>: default is false if this subcommand is omitted.
Line 128: Line 184:
 
| --zerohpmarker || <name(s) of status markers to apply if designated token bar is 0 after damage is applied> || comma delimited || style="text-align:center"|--- || <nowiki>--zerohpmarker|dead</nowiki> || <b>NOTE</b>: 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 || style="text-align:center"|--- || <nowiki>--zerohpmarker|dead</nowiki> || <b>NOTE</b>: if custom markers are used, you must include the whole id, e.g. name::####
 
|-
 
|-
| --resistAttr || <attrName> || If the damageType(s) string is included in the current value of the de<b>NOTE</b>d attribute, then the <nowiki>--resistanceRule</nowiki> will be applied || npc_resistances || <nowiki>--resistAttr|npc_resistances</nowiki> || <b>NOTE</b>: default is npc_resistances if this subcommand is omitted
+
| --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 || <nowiki>--removeAtZero|true</nowiki> || <b>NOTE</b>: default is false if this subcommand is omitted. <b>WARNING</b>: be careful with this, because if your player tokens are not ignored by the AoE (using the <code>--ignore</code> command), their tokens will be deleted!
 
|-
 
|-
| --vulnerableAttr || <attrName> || If the damageType(s) string is included in the current value of the de<b>NOTE</b>d attribute, then the <nowiki>--vulnerableRule</nowiki> will be applied || npc_vulnerabilities || <nowiki>--resistAttr|npc_vulnerabilities</nowiki> || <b>NOTE</b>: default is npc_vulnerabilities if this subcommand is omitted
+
| --resistAttr || <attrName> || If the damageType(s) string is included in the current value of the denoted attribute, then the <nowiki>--resistanceRule</nowiki> will be applied || npc_resistances || <nowiki>--resistAttr|npc_resistances</nowiki><br><nowiki>--resistAttr|npc_resistances, npcd_resistances</nowiki> || If multiple attribute names are supplied, the script will check <u>both</u><br><b>NOTE</b>: default is npc_resistances if this subcommand is omitted
 
|-
 
|-
| --immunityAttr || <attrName> || If the damageType(s) string is included in the current value of the de<b>NOTE</b>d attribute, then the <nowiki>--immunityRule</nowiki> will be applied || npc_immunities || <nowiki>--resistAttr|npc_immunities</nowiki> || <b>NOTE</b>: default is npc_immunities if this subcommand is omitted
+
| --vulnerableAttr || <attrName> || If the damageType(s) string is included in the current value of the denoted attribute, then the <nowiki>--vulnerableRule</nowiki> will be applied || npc_vulnerabilities || <nowiki>--vulnerableAttr|npc_vulnerabilities</nowiki><br><nowiki>--vulnerableAttr|npc_vulnerabilities, someOtherAttributeName</nowiki> || If multiple attribute names are supplied, the script will check <u>both</u><br><b>NOTE</b>: 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 no damage will be applied || npc_immunities || <nowiki>--immunityAttr|npc_immunities</nowiki><br><nowiki>--immunityAttr|npc_immunities, someOtherAttributeName </nowiki> || If multiple attribute names are supplied, the script will check <u>both</u><br><b>NOTE</b>: 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 || <nowiki>--resistanceRule|*0.5</nowiki><br><nowiki>--resistanceRule|/2</nowiki><br><nowiki>--resistanceRule|-10</nowiki> || <b>NOTE</b>: default is "*0.5" 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 || <nowiki>--resistanceRule|*0.5</nowiki><br><nowiki>--resistanceRule|/2</nowiki><br><nowiki>--resistanceRule|-10</nowiki> || <b>NOTE</b>: default is "*0.5" if this subcommand is omitted
Line 138: Line 196:
 
| --vulnerableRule || <expression> || The math operation (accepts single Operator and Operand) to be applied if target is vulnerable to damageType(s) || *2 || <nowiki>--vulnerableRule|*2</nowiki><br><nowiki>--vulnerableRule|+10</nowiki> || <b>NOTE</b>: default is "*2" 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 || <nowiki>--vulnerableRule|*2</nowiki><br><nowiki>--vulnerableRule|+10</nowiki> || <b>NOTE</b>: 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 || <nowiki>--immunityRule|*0</nowiki> || <b>NOTE</b>: default is *0 if this subcommand is omitted
+
| --resource|| <attrName>,optional <cost>,optional <alias> || Will deduct spell slots or other resources (mana, power points, etc.). || reduce by 1 || <nowiki>--resource|lvl1_slots_expended</nowiki><br><nowiki>--resource|lvl1_slots_expended,1</nowiki><br><nowiki>--resource|lvl1_slots_expended,1, Level 1 Slots</nowiki><br><nowiki>--resource|lvl?{Cast at what level?}_slots_expended,1,Level ?{Cast at what level?} Slots</nowiki><br> || <b>NOTE</b>: Current attribute values after deduction will be whispered in chat. If insufficient resources, an msg will be sent to chat and the AoE will not be generated.
 +
|-
 +
| --noSave|| <true/yes/1/false/no/0> || the save becomes hardcoded to 0 and the DC hardcoded to 99999 || 0 || <nowiki>--noSave|1</nowiki> || Often useful for healing spells
 
|-
 
|-
 
|}
 
|}
  
=== Chat Output Style Commands ===
+
== Chat Output Style Commands ==
 
{| class="wikitable mw-collapsible"
 
{| class="wikitable mw-collapsible"
 
|-
 
|-
Line 153: Line 213:
 
| --rightsub || <text> || subtitle of the chat output table. If leftsub is included, they will be separated by a diamond icon || style="text-align:center"|--- || <nowiki>--rightsub|some text</nowiki><br><nowiki>--rightsub|DC @{selected|spell_save_dc} DEX</nowiki> || style="text-align:center"|" "
 
| --rightsub || <text> || subtitle of the chat output table. If leftsub is included, they will be separated by a diamond icon || style="text-align:center"|--- || <nowiki>--rightsub|some text</nowiki><br><nowiki>--rightsub|DC @{selected|spell_save_dc} DEX</nowiki> || style="text-align:center"|" "
 
|-
 
|-
| --desc || <text> || optional description at bottom of chat output table || style="text-align:center"|--- || <nowiki>--desc| Line 1 of the description%br%Line 2 of the description</nowiki> || style="text-align:center"|" "
+
| --desc || <text> || optional description at bottom of chat output table || style="text-align:center"|--- || <nowiki>--desc| Line 1 of the description%br%Line 2 of the description</nowiki> || <b>NOTE:</b> If you want to add line breaks in your description, use the code <b>%br%</b> 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 || <nowiki>--hidenames|1</nowiki> || style="text-align:center"|" "
 
| --hideNames || <true/yes/1/false/no/0> || If true, the token names will be replaced with Target_# || FALSE || <nowiki>--hidenames|1</nowiki> || style="text-align:center"|" "
 
|-
 
|-
| --titlecardbackground || <html> || Value to set the html "background-image:" property of the title field of the chat output || linear-gradient(red, yellow) || <nowiki>--titlecardbackground|linear-gradient(#995522, blue)</nowiki><br><nowiki>--titlecardbackground|url('https://s3.amazonaws.com/files.d20.io/images/</nowiki><br><nowiki>248538196/c9xft1A2jWNqOkBsxQwyLQ/max.jpg?1633278017');background-size: contain;</nowiki> || style="text-align:center"|" "
+
| --titlecardbackground || <html> || Value to set the html "background-image:" property of the title field of the chat output || linear-gradient(red, yellow) || <nowiki>--titlecardbackground|linear-gradient(#995522, blue)</nowiki><br><nowiki>--titlecardbackground|url('https://s3.amazonaws.com/files.d20.io/images/</nowiki><br><nowiki>248538196/c9xft1A2jWNqOkBsxQwyLQ/max.jpg?1633278017');background-size: 100% 100%; background-repeat: no-repeat;</nowiki> || 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 || <nowiki>--titlefontface|Contrail One</nowiki> || style="text-align:center"|" "
 
| --titlefontface || <fontName> || Font style of the chat output title || Contrail One || <nowiki>--titlefontface|Contrail One</nowiki> || style="text-align:center"|" "
Line 194: Line 254:
 
|-
 
|-
 
| --oddrowfontcolor || <#RRGGBB> || Font color of the odd rows of the Save fields || #000000 || <nowiki>--oddrowfontcolor|#000000</nowiki> || style="text-align:center"|" "
 
| --oddrowfontcolor || <#RRGGBB> || Font color of the odd rows of the Save fields || #000000 || <nowiki>--oddrowfontcolor|#000000</nowiki> || style="text-align:center"|" "
 +
|-
 +
| --chatAvatarsEnabled|| <0/1/yes/no/true/false> || This will adjust the left margin of the table output appropriately if chat menu avatars are disabled via in-game settings. || true|| <nowiki>--chatAvatarsEnabled|0</nowiki><br><nowiki>--chatAvatarsEnabled|false</nowiki><br><nowiki>--chatAvatarsEnabled|no</nowiki><br> || style="text-align:center"|
 +
|-
 +
| --whisperAll|| <0/1/yes/no/true/false> || Setting to true will whisper all chat output|| false|| <nowiki>--whisperAll|1</nowiki> || style="text-align:center"|
 +
|-
 +
| --whisperResults|| <0/1/yes/no/true/false> || Setting to true will whisper only the saving throw and damage application notifications|| false|| <nowiki>--whisperResults|1</nowiki> || style="text-align:center"|
 
|-
 
|-
 
|}
 
|}
  
== Chat Output Cheat Sheet==
+
= 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
 
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
 +
 +
<b>NOTE:</b> 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.
 +
 +
<b>NOTE:</b> 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.
  
 
[[File:SmartAoE Chat Output - Cheat Sheet.jpg|900px]]
 
[[File:SmartAoE Chat Output - Cheat Sheet.jpg|900px]]
  
== Quirks, Recommendations, Known Issues and/or TBD==
+
= 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 <code>--aoeType|</code> command.
+
#'''(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 <code>--aoeType|</code> 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 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 the next bullet point:
+
##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 <nowiki>#1</nowiki> below'''
*'''(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.
+
#'''(Quirk)''' For float AoE types, if <code>--forceIntersection</code> is omitted, the default behavior is to force the controlToken to intersection, <b>unless the AoE size is one square or less</b>. 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 <code>--forceIntersection</code> is explicitly set, the script will honor the explicit setting.
*'''(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.
+
#'''(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 <code>--minGridArea|0.25</code> and <code>--origin|nearest, face</code>. 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 <code>--minGridArea|0.5</code>, <code>--origin|nearest</code>, and <code>--forceIntersection|1</code> to better approximate the rules.  
+
#'''(Recommendation)''' For 5e-style cones, I recommend starting with <code>--minGridArea|0.25</code> and <code>--origin|nearest, face</code>. 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.  
*'''(Issue)''' 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 <code>--minGridArea</code> 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 rules that work within the SmartAoE framework)
+
#'''(Recommendation)''' For PF-style cones, I recommend starting with <code>--minGridArea|0.5</code>, <code>--origin|nearest</code>, and <code>--forceIntersection|1</code> to better approximate the rules. '''However, see issue <nowiki>#4</nowiki> below.'''
*'''(Issue)''' Resistance/immunity rules are simple at present. If a creatures is resistant to, say, piercing damage from non-magical sources, there is currently no way to 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.
+
#'''(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. <code>--dc|[[@{selected|spell_save_dc}]]</code>
 +
#<strike>'''(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.</strike> (fixed!)
 +
#<strike>'''(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 <code>--minGridArea</code> 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)</strike> (fixed with new PFcone and PFcircle aoeTypes!)
 +
#'''(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.
  
== Example SmartAoE Macros==
+
==Using custom images for title background==
====<u>Simple 20ft cube</u>====
+
The <code>--titlecardbackground</code> subcommand will accept css styling information to further personalize the chat output. One fun trick is to utilize an image for your title background.<br>
 +
Example:
 +
<code>--titlecardbackground|url(<nowiki>'https://s3.amazonaws.com/files.d20.io/images/248538196/c9xft1A2jWNqOkBsxQwyLQ/max.jpg?1633278017'</nowiki>);background-size: 100% 100%; background-repeat: no-repeat;</code>
 +
 
 +
[[File:SmartAoE example - custom image background - Lightning Bolt.jpg|300px]] <br>
 +
If you have something in your image library that you want to use, [https://app.roll20.net/users/104025/the-aaron 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 <code>!GetTokenUrl</code>. The URL will be printed to chat!
 +
 
 +
<pre>
 +
(() => {
 +
    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/>'));
 +
        }
 +
    });
 +
})();
 +
</pre>
 +
 
 +
= Example SmartAoE Macros=
 +
 
 +
<b>I have put several 5e spell macros in a GitHub repo here:</b>[[https://github.com/djmoorehead/SmartAoE-Macros/tree/main Spell Repo]]
 +
 
 +
==<u>Simple 20ft cube</u>==
 +
[[File:SmartAoE Simple 20ft Cube.gif|450px|thumb|class=resize]]
 
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.  
 
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.  
[[File:SmartAoE Simple 20ft Cube.gif|450px|thumb]]
+
 
<pre style="white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; white-space:pre-wrap; word-wrap:break-word;width:900px;">
+
<pre>
 
!smartaoe {{
 
!smartaoe {{
 
   --aoeType|square, float
 
   --aoeType|square, float
Line 224: Line 328:
 
}}
 
}}
 
</pre>
 
</pre>
 +
<br clear=right>
  
<br><br><br><br><br><br><br><br>
+
==<u>Simple variable cube</u>==
 
+
====<u>Simple variable cube</u>====
+
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.
+
 
[[File:SmartAoE Simple Variable Cube.gif|450px|thumb]]
 
[[File:SmartAoE Simple Variable Cube.gif|450px|thumb]]
<pre style="white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; white-space:pre-wrap; word-wrap:break-word;width:900px;">
+
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.
 +
 
 +
<pre>
 
!smartaoe {{
 
!smartaoe {{
 
   --aoeType|square
 
   --aoeType|square
Line 237: Line 341:
 
</pre>
 
</pre>
  
<br><br><br><br><br><br><br><br>
+
<br clear=right>
  
====<u>Pathfinder-style 30ft cone approximation</u>====
+
==<u>Simple 5e fixed-radius cone with origin rotation</u>==
Attempt at getting close to PF-style cone rules. Forced controlToken to grid intersection, only allowed token corners for origin point, and required 50% of grid area coverage required (<code>--minGridArea|0.5</code>). Any more than 50% will start to omit the squares closest to the sourceToken. This is as close as I could get to PF rules, although there are some extra squares counted. The PF ruleset does not appear to weigh percentage of grid area evenly.  
+
[[File:SmartAoE Cone Origin Rotation (optimized).gif|450px|thumb]]
[[File:SmartAoE PF 30ft cone approximation.gif|450px|thumb]]
+
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 <code>!smartrotateorigin ccw<nowiki>/</nowiki>cw</code> commands in separate ability macros (described in [[#Initial Setup/Install]]. Both the token corners and faces are valid origin points due to the command <code>--origin|nearest, face</code> in the AoE-generation macro.
<pre style="white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; white-space:pre-wrap; word-wrap:break-word;width:900px;">
+
 
 +
<pre>
 
!smartaoe {{
 
!smartaoe {{
   --aoeType|cone, 90
+
   --aoeType|5econe
 
   --radius|30ft
 
   --radius|30ft
 +
  --minGridArea|0.25
 +
  --origin|nearest, face
 +
}}
 +
</pre>
 +
<pre>
 +
!smartrotateorigin ccw
 +
</pre>
 +
<pre>
 +
!smartrotateorigin cw
 +
</pre>
 +
 +
<br clear=right>
 +
 +
==<u>True radius 15ft cone approximation</u>==
 +
[[File:SmartAoE true radius cone example.gif|450px|thumb]]
 +
The following uses the "cone" aoeType, with a query determining the cone radius. Radius is fixed at 15ft.
 +
 +
<pre>
 +
!smartaoe {{
 +
  --aoeType|cone, ?{Enter cone angle|90}
 +
  --radius|15ft
 
   --minGridArea|0.50
 
   --minGridArea|0.50
   --origin|nearest
+
   --origin|nearest, face
  --forceIntersection|1
+
 
}}
 
}}
 
</pre>
 
</pre>
 +
<br clear=right>
  
<br><br><br><br><br>
+
==<u>Pathfinder 20ft circle</u>==
 +
[[File:SmartAoE Pathfinder 40ft circle example.gif|450px|thumb]]
 +
The "PFcircle" aoeType performs an additional filter to the affected grid squares, using the "every other diagonal counts as 2 squares" rule.
 +
 
 +
<pre>
 +
!smartaoe {{
 +
  --aoeType|PFcircle, float
 +
  --radius|20ft
 +
  --minGridArea|0.50
 +
}}
 +
</pre>
 +
<br clear=right>
  
====<u>5e Burning Hands</u>====
+
==<u>Burning Hands (5e)</u>==
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.  
+
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. <b>NOTE</b>: The macro below includes <code>--instant|1</code>, but the animated gif was created before that command was available.
 
[[File:SmartAoE Burning Hands 1200x650.gif|450px|thumb]]
 
[[File:SmartAoE Burning Hands 1200x650.gif|450px|thumb]]
<pre style="white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; white-space:pre-wrap; word-wrap:break-word;width:900px;">
+
<pre>
 
!smartaoe {{
 
!smartaoe {{
 
   --title|Burning Hands
 
   --title|Burning Hands
Line 275: Line 412:
 
   --damageType1|Fire
 
   --damageType1|Fire
 
   --ignore|SmartAOE_ignore,1  
 
   --ignore|SmartAOE_ignore,1  
 +
  --instant|1
  
 
   --autoApply|1
 
   --autoApply|1
Line 285: Line 423:
 
</pre>
 
</pre>
  
====<u>5e Faerie Fire</u>====
+
==<u>Faerie Fire (5e)</u>==
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. The condition marker is from a custom uploaded marker set, so the full marker name::number must be included in the <code>--conditionFail</code> command. Chat output is customized for a more Fae-appropriate theme.
+
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 <b>custom uploaded marker set</b>, the full marker <code>name::number</code> must be included in the <code>--conditionFail</code> 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. <b>NOTE</b>: The macro below includes <code>--instant|1</code>, but the animated gif was created before that command was available.
 
[[File:SmartAoE Faerie Fire 1200x650.gif|450px|thumb]]
 
[[File:SmartAoE Faerie Fire 1200x650.gif|450px|thumb]]
<pre style="white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; white-space:pre-wrap; word-wrap:break-word;width:900px;">
+
<pre>
 
!smartaoe {{
 
!smartaoe {{
 
   --title|Faerie Fire
 
   --title|Faerie Fire
Line 308: Line 446:
 
   --saveFormula|5eDEX  
 
   --saveFormula|5eDEX  
 
   --autoApply|1
 
   --autoApply|1
 +
  --instant|1
 
   --conditionFail|GrantsAdvantage::1510122
 
   --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.
 
   --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.
Line 313: Line 452:
 
</pre>
 
</pre>
  
====<u>5e Ice Storm</u>====
+
==<u>Ice Storm (5e)</u>==
This macro creates a 20ft radius "floating" circle using a custom multi-sided token from a character called "GenericSpellAoE" 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.[[File:SmartAoE Ice Storm 922x499.gif|450px|thumb]]
+
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. <b>NOTE</b>: The macro below includes <code>--instant|1</code>, but the animated gif was created before that command was available. [[File:SmartAoE Ice Storm 922x499.gif|450px|thumb]]
<pre style="white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; white-space:pre-wrap; word-wrap:break-word;width:900px;">
+
<pre>
 
!smartaoe {{
 
!smartaoe {{
 
   --title|Ice Storm
 
   --title|Ice Storm
Line 343: Line 482:
 
   --damageType2|Cold
 
   --damageType2|Cold
 
   --ignore|SmartAoE_Ignore,1  
 
   --ignore|SmartAoE_Ignore,1  
 +
  --instant|1
  
 
   --autoApply|0
 
   --autoApply|0
Line 350: Line 490:
 
}}
 
}}
 
</pre>
 
</pre>
 +
 +
==<u>Spirit Guardians (5e)</u>==
 +
A couple of different concepts in this macro. First, since the spell is centered on and moves with the caster, the command <code>--controlTokName|self</code> 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 <code>--forceIntersection|0</code>. 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 <code>--aoeType|square, float</code>. Since the spell effects are triggered at the <b>start</b> 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 (<code>!smartquery @{target|Choose a Target|token_id}</code>). When the spell ends, it is removed with another alternate command (<code>!smartremove</code>). Both of these commands have been added as an ability on the caster token.<br><br>
 +
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.  [[File:SmartAoE Spirit Guardians Example.gif|450px|thumb]]
 +
<pre>
 +
!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
 +
}}
 +
</pre>
 +
 +
==<u>Aura of Vitality (5e Healing)</u>==
 +
This macro is nearly identical to the Spirit Guardians macro above (read description for explanations). The only differences are: 1)the <code>--noSave|1</code> 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)<b>the damageFormula results in a negative number!</b> (this is interpreted as healing by the script), 4)no tokens are ignored, and 5)the title background image URL is different.<br><br>
 +
<b>Note:</b> 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 <code>[[0 + <nowiki>[[0 - 2]]</nowiki>d6]]</code> syntax for the healing "damage"<br><br>
 +
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. <br><br>
 +
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 ;) [[File:SmartAoE Aura of Vitality Example.gif|450px|thumb]]
 +
<pre>
 +
!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
 +
}}
 +
</pre>
 +
 +
==<u>Wall of Fire (5e)</u>==
 +
[[File:SmartAoE Wall of Fire (5e) Example.gif|450px|thumb]]
 +
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.
 +
 +
<pre>
 +
!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
 +
}}
 +
</pre>
 +
<br clear=right>
 +
 +
==<u>Custom Ruleset Example, 180deg cone</u>==
 +
The following example shows how to implement a custom ruleset via deferral characters in the <code>--saveFormula</code> 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 <nowiki>[[...]]</nowiki> 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 <code>--damageSaveRule|*0</code>. 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 <code>--radius</code> and <code>--rightsub</code> commands. [[File:SmartAoE Custom Rules Example.gif|450px|thumb]]
 +
<pre>
 +
!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
 +
}}
 +
</pre>
 +
 +
= [For script writers] Registering your token change events to SmartAoE =
 +
<b>Script writers can now register their token change events to SmartAoE!</b>
 +
 +
For example, the {{forum|post/2139713/script-aura-slash-tint-healthcolor Aura/Tint HealthColor}} 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:
 +
<pre>
 +
            if('undefined' !== typeof SmartAoE && SmartAoE.ObserveTokenChange){
 +
                SmartAoE.ObserveTokenChange(function(obj,prev){
 +
                    myEventHandlerFunction(obj, prev);
 +
                });
 +
            };
 +
</pre>
 +
So, extending the example of {{forum|post/2139713/script-aura-slash-tint-healthcolor Aura/Tint HealthColor}}, the new registerEventHandlers function in that script would read as follows:
 +
<pre>
 +
    //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");
 +
                });
 +
            };
 +
        };
 +
</pre>
 +
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 ;)
 +
 +
[[Category:User API Scripts]]
 +
[[Category:Featured articles]]
 +
[[Category:Maps]]

Latest revision as of 19:55, 28 January 2024

Main Page: API:Script Index

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

[edit] 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


[edit] 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 :)

[edit] Initial Setup/Install

  1. Install the SmartAoE api script, currently available for one-click install or found on GitHub here: v0.28
  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

[edit] 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.

[edit] List of Subcommands

[edit] 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 --aoeOutlineColor|#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
--spawnSound <trackName> plays the specified jukebox track when the control token is spawned --- --spawnSound|Thundercrack
--moveSound <trackName> plays the specified jukebox track when the control token is moved --- --moveSound|Whoosh
--triggerSound <trackName> plays the specified jukebox track when the control token is triggered --- --triggerSound|Fireball
--deleteSound <trackName> plays the specified jukebox track when the control token is removed from the map --- --deleteSound|Wilhelm scream
--tooltip <text> adds a tooltip to the control token. Useful if multiple persistent effects are present on the same map --- --tooltip|Stinking Cloud
--turnorder <name>,<optional value>,<optional formula>,<optional true/yes/1/false/no/0> populate a custom turnorder entry when the control token is spawned, so you can more easily track durations, etc. --- --turnorder|Stinking Cloud,0,+1 //current value 0, adds 1 every turn, default condition in effect to link entry to controlTok
--turnorder|Stinking Cloud,0,1, true //same as above, note the plus sign is not necessary, and this time we are explicitly linking to the controlTok
--turnorder|Hold Person,10,-1, false //current value 10, subtracts 1 every turn, entry not linked to controlTok
You must specify a name, and then a current value, a formula, and a switch to determine if the entry is tied to the control token are optional. If tied to the control token, then the entry will be deleted when the control token is deleted. Otherwise you will have to delete the entry manually. The default behavior is to link to the control token.
--affectsCaster <true/yes/1/false/no/0> allows the caster to be affected by the AoE false --affectsCaster|true NOTE: default is 0 if this subcommand is omitted
--casterCondition <statusmarker>, <optional "onFail"> add a statusmarker to the source (usually caster) token --- --casterCondition|stopwatch
//adds a stopwatch marker to the caster regardless of the saving throw results
--casterCondition|stopwatch, onFail
//adds a stopwatch marker to the caster only if at least one target fails
Useful for 5e concentration markers, etc.

[edit] 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 no damage 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
--resource <attrName>,optional <cost>,optional <alias> Will deduct spell slots or other resources (mana, power points, etc.). reduce by 1 --resource|lvl1_slots_expended
--resource|lvl1_slots_expended,1
--resource|lvl1_slots_expended,1, Level 1 Slots
--resource|lvl?{Cast at what level?}_slots_expended,1,Level ?{Cast at what level?} Slots
NOTE: Current attribute values after deduction will be whispered in chat. If insufficient resources, an msg will be sent to chat and the AoE will not be generated.
--noSave <true/yes/1/false/no/0> the save becomes hardcoded to 0 and the DC hardcoded to 99999 0 --noSave|1 Often useful for healing spells

[edit] 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 " "
--chatAvatarsEnabled <0/1/yes/no/true/false> This will adjust the left margin of the table output appropriately if chat menu avatars are disabled via in-game settings. true --chatAvatarsEnabled|0
--chatAvatarsEnabled|false
--chatAvatarsEnabled|no
--whisperAll <0/1/yes/no/true/false> Setting to true will whisper all chat output false --whisperAll|1
--whisperResults <0/1/yes/no/true/false> Setting to true will whisper only the saving throw and damage application notifications false --whisperResults|1

[edit] 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

[edit] 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.

[edit] 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/>'));
        }
    });
})();

[edit] Example SmartAoE Macros

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

[edit] 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
}}


[edit] 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
}}


[edit] 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


[edit] 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
}}


[edit] 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
}}


[edit] 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.
}}

[edit] 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.
}}

[edit] 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.
}}

[edit] 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
}}

[edit] 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
}}

[edit] 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
}}


[edit] 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
}}

[edit] [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 ;)