Skip to content

token.applyDamage()

Propose damage or healing. The change appears in chat as a card someone approves before any hit points move.

Part of the Action API · Function reference

Available on token, target, and every entry of targets.

Signature

token.applyDamage(amount: number, damageType?: string): void

Synchronous, returns nothing. The effect is a message in chat, not a change to the token.

Parameters

Parameter Type Description
amount number Positive damages, negative heals. Non-numeric values become 0.
damageType string Optional. Which damage type this is — an id or name from damageTypes. Drives resistance and vulnerability. Omit for typeless damage.

As a convenience, a single object argument is also accepted, so the result of a prompt() form can be passed straight through:

const answer = await prompt('<label>Damage <input name="amount" type="number"></label>');
target.applyDamage(answer);            // reads answer.amount and answer.damageType

Returns

Nothing.

There is no way to learn from script whether the damage was approved, modified, or denied — approval happens after your action has finished. An action cannot branch on the outcome.

Damage is proposed, not applied

applyDamage() never touches hit points directly. It calculates the result and emits a card into chat showing:

  • the raw amount and the damage type
  • every resistance, vulnerability, and immunity that applied, itemised
  • the final amount
  • current HP, and what it would become

The GM or the token's owner then approves, modifies, or denies it. Hit points change on approval, and the combat log records it then.

This is deliberate. An action can propose damage; it cannot silently rewrite someone else's character. There is no flag, option, or trick that bypasses it from a script — the auto-approving path exists only for the GM's own heal/harm hotkey.

Damage

With a positive amount, the target's damage modifiers are applied first. Those come from the token's damageModifiers, which is what makes a fire-immune creature take nothing from your fireball without you writing any of that logic.

target.applyDamage(14, 'fire');
target.applyDamage(roll('2d6+3', { label: 'Greataxe' }), 'slashing');

The projected new HP is floored at zero.

Healing

A negative amount heals. Damage modifiers are skipped — nobody is resistant to being healed — and the card is presented as healing.

token.applyDamage(-5);                        // heal 5
token.applyDamage(-roll('2d4+2', { label: 'Cure Wounds' }));

Healing is capped at the token's hp_max when it has one. A damageType on a heal is ignored.

Examples

Attack against AC

const attack = roll('1d20+attackBonus', { label: 'Attack' });
if (!target) return 'No target.';
if (attack >= (target.getProperty('ac') ?? 10)) {
  target.applyDamage(roll('1d8+3', { label: 'Damage' }), 'slashing');
} else {
  sendMessage(`${token.name} misses ${target.name}.`);
}

One roll, many targets

const damage = roll('8d6', { label: 'Fireball' });
for (const t of targets) {
  t.applyDamage(damage, 'fire');
}

Half damage on a successful save

const dc = 15;
const damage = roll('8d6', { label: 'Fireball' });
for (const t of targets) {
  const save = await t.requestRoll('1d20+dex', { label: 'Dex save' });
  if (save === null) continue;                 // they declined
  t.applyDamage(save >= dc ? Math.floor(damage / 2) : damage, 'fire');
}

Ask how much, then apply it

const answer = await prompt(`
  <label>Damage <input name="amount" type="number" data-autofocus></label>
  <label>Type <select name="damageType">
    ${damageTypes.map((d) => `<option value="${d.id}">${d.name}</option>`).join('')}
  </select></label>
`);
if (answer === null) return;
target.applyDamage(answer.amount, answer.damageType);

Notes and gotchas

  • One card per call. Ten targets means ten cards to approve. That is intentional — each is a separate decision — but consider whether your action really needs to hit ten things.
  • Roll once, apply many. Calling roll() inside the loop rolls separately for each target. Roll before the loop unless you want that.
  • Nothing is guaranteed to happen. The card can be denied. Do not write an action that assumes the HP moved.
  • Type names must match your campaign's damage types for resistances to apply. Read the damageTypes array rather than hard-coding strings.
  • In the Test panel the card is captured, not sent, so nothing can be approved and no HP changes.

See also