Macro Effects in FoundryVTT
Recently, I’ve been running my long standing D&D 5e game using Foundry VTT and trying to expand/improve my combat encounters through automation. I have a fight coming up where the boss has a very specific regeneration ability that heals him 10% of his total health at the start of each of his turns until he takes fire damage. Looking around at the various modules I already had installed I figured this was possible, I just wasn’t sure of the specifics. After some research I’ve settled on three modules that are essential to getthing this effect to work:
The first step was obviously to create a new item in Foundry with the “Feature” type since this property was going to be inherent to the creature. After picking out an item and adding some description text I headed straight for the “Effects” tab. Since this is a passive buff I went down to “Passive Effects” and clicked “Add” to create my super awesome new effect. Unfortunately, this is where the problems started.
Under “Duration” I selected the “Start of each turn” option for “Macro Repeat”
and added a special duration of expiring when the character takes fire damage.
Seems simple, right? The issue came at the “Effects” tab and figuring out what
combination of attribute, mode, and value were needed to get things to actually
work. Originally I tried an “ADD” to the property attributes.hp.value
and while
I was able to get the 10% health increase by writing some JS in the value field,
I wasn’t able to get the effect to repeat. When testing the creature would get a
flat 10% heal when the feature was added and lose it when it was removed, not get
healed each turn.
After a lot of poking around, I struck upon the solution…the “Macro” portion
of the “Macro Repeat” option was more specific than I originally had assumed. I
had taken it to mean the entire effect was the “macro” while it meant specifically
a defined Foundry macro it was trying to run! This changed my effect to be using
the attribute macro.execute
, a mode of Custom
, and a value of
baerdan.effects.regenerate
. The macro JS is shown below and can be named anything,
I just picked a namespace-esq title to make it easier to sort/organize my macros.
/******************************************************************************
* baerdan.effects.regenerate
*
* This macro does a percentage based heal on any actor it is run against, up
* to their max defined health. As far as I know it only works for actors with
* linked tokens. This means things like big bosses or other singleton critters,
* not any old mook in the fight.
*
* I mostly use this through Dynamic Active Effects (DAE) which automatically
* passes a range selector (eg. "each"), then any user defined parameters
* (eg. heal percentage), and finally an object containing info on the calling
* token. In context, this is the reason for the somewhat "magic" array offsets
* used later to grab values.
*
* You can adjust the heal rate by providing an integer percentage that you want
* the creature to heal per execution of the macro as the first parameter to the
* macro in the DAE effect. If the parameter is omitted the default is 10% of
* max HP per execution.
*****************************************************************************/
let heal_percent = (typeof args[1] === "number") ? (args[1]/100) : 0.1;
if (actor != null) {
const actor_data = actor.data.data;
let heal = Math.floor(actor.data.attributes.hp.max * heal_percent);
let hp = Math.min(actor_data.attributes.hp.value + heal, actor_data.attributes.hp.max);
await actor.update({"data.attributes.hp.value": hp});
}
In writing the JS I learned two additional bits of information. First, you must
include the actor != null
check to avoid nasty error messages when you attach
this item to a character sheet that doesn’t currently have a token on the battlefield.
Second, you can update properties on the actor
object until you’re blue in the
face but they won’t actually be persistent until you set them with a call to
await actor.update()
. The await
portion is to make the call asyncronous, I
guess to avoid blocking the UI while it runs. So there you have it, a scary
monster ability which will keep it healing as your party tries to beat it down.