
DmgTypeFX v1.2 by Surok updated 8-25-25 Description:
DmgTypeFX monitors chat for damage type keywords (e.g., acid, fire, etc.) and triggers the corresponding FX when a token’s HP (bar1) decreases. The FX that are triggered are based on the most recent chat message —so whatever damage type keywords appear in the latest non‑API chat message determine the FX. Additionally, the API provides a configuration menu (whispered to the GM) with buttons for each damage type. Clicking a button brings up preset drop‑down menus (for Shape and Color) to update that damage type's FX mapping. How to Use:
Installation:
Copy and paste the code below into your Roll20 API Scripts.
FX Triggering:
The API continuously scans chat for damage type keywords. When a token’s HP (bar1) decreases (or via token‑mod commands), the FX corresponding to the keywords in the most recent chat message will trigger on that token.
Configuration Menu:
In chat, type:
!DmgTypeFX-menu
This will whisper a configuration menu to the GM with one button per damage type and a “Set Trigger Bar” option.
Clicking a damage type button launches drop‑down menus to select a Shape and Color (from preset options), which updates that damage type’s FX mapping.
Use the “Set Trigger Bar” button to change which token bar (bar1, bar2, or bar3) is monitored for HP changes.
Help:
In chat, type:
!DmgTypeFX-help
This displays detailed instructions (whispered to the GM) along with a “Return” button to go back to the main menu.
Notes:
Only manual changes on bar1 trigger FX; for bar2 or bar3, use token‑mod commands on selected tokens.
The API always uses the most recent chat output to determine which damage type (and thus which FX) to trigger.
Below is the complete code for DmgTypeFX v1.2: /*
DmgTypeFX
Version: 1.2 (ES5 compatible)
Author: Surok (<a href="https://app.roll20.net/users/335573/surok" rel="nofollow">https://app.roll20.net/users/335573/surok</a>)
API COMMANDS (quick reference)
!DmgTypeFX-menu
- Opens the GM-only configuration menu (buttons for each damage type + "heal").
- Clicking a button launches drop-downs to set Shape/Color for that type.
!DmgTypeFX-config [type] [shape] [color]
- Updates the FX mapping for the given type.
- Example: !DmgTypeFX-config fire explode fire
- Types include: acid, bludgeoning, cold, fire, force, lightning, poison, thunder,
radiant, necrotic, psychic, piercing, slashing, heal
!DmgTypeFX-help
- Shows left-aligned instructions (GM-only) with a Return button.
BEHAVIOR
• Uses the most recent non-API chat message to detect damage types (e.g., "fire", "acid").
• On HP decrease (bar1): Triggers FX for all detected damage types.
• On HP increase (bar1): Triggers the configurable HEAL FX (default: glow-slime).
• TokenMod support: FX triggers on bar1 decreases/increases via "!token-mod --set bar1_value|±N".
GLOBAL HOOKS (for other scripts)
DmgTypeFX.TriggerFx(token) -> plays damage FX based on last-detected chat types
DmgTypeFX.TriggerHealFx(token) -> plays the current heal FX
*/
// Assign the script to a global variable so other scripts can access it.
var DmgTypeFX = DmgTypeFX || (function() {
"use strict";
// === Basic UI Styling for Chat Menus ===
var containerStyle = "border: 2px solid black; border-radius: 4px; " +
"box-shadow: 1px 1px 1px #707070; text-align: center; " +
"padding: 3px 0; margin: 0 auto; color: #000; " +
"background-image: -webkit-linear-gradient(-45deg, #a7c7dc 0%, #85b2d3 100%);";
var headerStyle = "text-align: center; margin: 0 0 10px;";
var smallButtonStyle = "padding: 1px; text-align: center; font-size: 9pt; " +
"width: 48px; height: 14px; border: 1px solid black; " +
"margin: 1px; border-radius: 4px; box-shadow: 1px 1px 1px #707070; " +
"color: white; text-decoration: none; display: inline-block;";
var menuButtonStyle = "display: block; text-decoration: none; color: white; " +
"background-color: #6FAEC7; padding: 5px; border-radius: 4px; " +
"box-shadow: 1px 1px 1px #707070; margin-bottom: 5px;";
// Main menu navigation block with a Help button.
var mainNavBlock = '<div style="text-align: center; margin-top: 10px;">' +
'<a href="!DmgTypeFX-help" style="' + smallButtonStyle + '">Help</a>' +
'</div>';
// Help menu navigation block with a "Return" button.
var helpNavBlock = '<div style="text-align: center; margin-top: 10px;">' +
'<a href="!DmgTypeFX-menu" style="' + smallButtonStyle + '">Return</a>' +
'</div>';
function buildMenu(title, content, nav) {
return '<div style="' + containerStyle + '">' +
'<h3 style="' + headerStyle + '">' + title + '</h3>' +
'<div>' + content + '</div>' +
nav +
'</div>';
}
// === Global Variables & FX Mapping ===
// Stores the last detected damage type words from chat.
var lastDetectedDamageTypes = [];
// Mapping for 5e damage types to FX effect sequences.
// Each value is a semicolon-separated list of FX names.
var damageFxMap = {
acid: "bubbling-acid", // Bubbling Acid
bludgeoning: "bomb-blood", // Bomb Blood
cold: "burst-frost", // Burst Frost
fire: "explode-fire", // Explode Fire
force: "bomb-magic", // Bomb Magic
lightning: "splatter-magic", // Splatter Magic
poison: "bubbling-slime", // Bubbling Slime
thunder: "nova-smoke", // Nova Smoke
radiant: "glow-holy", // Glow Holy
necrotic: "glow-death", // Glow Death
psychic: "nova-magic", // Nova Magic
piercing: "pooling-blood", // Pooling Blood
slashing: "splatter-blood", // Splatter Blood
// Heal FX (default). Changeable via the same config UI.
heal: "glow-slime"
};
var damageTypeRegex = /\b(acid|bludgeoning|cold|fire|force|lightning|poison|thunder|radiant|necrotic|psychic|piercing|slashing)\b/gi;
function playEffects(token, fxStr) {
if (!fxStr) return;
var fxArray = fxStr.split(";");
fxArray.forEach(function(fx, index) {
setTimeout(function() {
spawnFx(token.get("left"), token.get("top"), fx.trim(), token.get("pageid"));
}, index * 250); // Adjust delay between FX as needed.
});
}
// Exposed function: damage FX for last-detected types
function triggerFxForToken(token) {
if (!token) return;
if (lastDetectedDamageTypes.length > 0) {
log('DmgTypeFX: Triggered for token ' + token.get('name') +
'. Applying FX for: ' + lastDetectedDamageTypes.join(', '));
lastDetectedDamageTypes.forEach(function(dt) {
if (damageFxMap.hasOwnProperty(dt)) {
var fxSequence = damageFxMap[dt];
playEffects(token, fxSequence);
}
});
}
}
// Exposed function: heal FX
function triggerHealFxForToken(token) {
if (!token) return;
if (damageFxMap.heal) {
log('DmgTypeFX: Heal triggered for token ' + token.get('name') +
'. Applying FX: ' + damageFxMap.heal);
playEffects(token, damageFxMap.heal);
}
}
// === Damage / Heal Detection ===
// Track last damage types seen in chat.
on("chat:message", function(msg) {
if (msg.type === "api") return;
var matches = [];
var regex = new RegExp(damageTypeRegex.source, "gi");
var m;
while ((m = regex.exec(msg.content)) !== null) {
var dt = (m[1] || "").toLowerCase();
if (matches.indexOf(dt) === -1) {
matches.push(dt);
}
}
if (matches.length > 0) {
lastDetectedDamageTypes = matches;
}
});
// Manual HP change (bar1): damage on decrease, heal on increase
on("change:graphic", function(token, prev) {
var prevHP = parseInt(prev.bar1_value, 10) || 0;
var currentHP = parseInt(token.get("bar1_value"), 10) || 0;
if (currentHP < prevHP) {
// Damage FX for last detected chat types
triggerFxForToken(token);
} else if (currentHP > prevHP) {
// Heal FX on increase
triggerHealFxForToken(token);
}
});
// TokenMod HP change (bar1): handle both decreases and increases
on("chat:message", function(msg) {
if (msg.type !== "api" || msg.content.indexOf("!token-mod") !== 0) return;
if (msg.content.indexOf("--set bar1_value|") === -1) return;
// Supports absolute and relative (|+N / |-N) forms; captures + or - if present.
var regexCmd = /--set\s+bar1_value\|([+-]?\d+)/i;
var match = msg.content.match(regexCmd);
if (!match) return;
var valueChange = parseInt(match[1], 10);
if (msg.selected && msg.selected.length > 0) {
msg.selected.forEach(function(sel) {
var token = getObj("graphic", sel._id);
if (!token) return;
if (valueChange < 0) {
// Damage
triggerFxForToken(token);
} else if (valueChange > 0) {
// Heal
triggerHealFxForToken(token);
}
});
}
});
// === Configuration Menu Commands ===
// Main menu (GM whisper)
on("chat:message", function(msg) {
if (msg.type !== "api") return;
var args = msg.content.split(/\s+/);
if (args[0] === "!DmgTypeFX-menu" && args.length === 1) {
var list = '<ul style="list-style: none; padding: 0; margin: 0; text-align: center;">';
for (var dt in damageFxMap) {
if (!damageFxMap.hasOwnProperty(dt)) continue;
list += '<li><a href="!DmgTypeFX-config ' + dt + ' ' +
'?{Choose Shape for ' + dt + '|Breath,breath|Beam,beam|Rocket,rocket|Burn,burn|Glow,glow|Sparkle,sparkle|Shield,shield|Bomb,bomb|Explode,explode|Burst,burst|Bubbling,bubbling|Nova,nova|Splatter,splatter|Pooling,pooling|Missile,missile} ' +
'?{Choose Color for ' + dt + '|Fire,fire|Charm,charm|Acid,acid|Death,death|Holy,holy|Blood,blood|Frost,frost|Slime,slime|Smoke,smoke|Water,water|Magic,magic}" style="' + menuButtonStyle + '">' +
dt + '</a></li>';
}
list += '</ul>';
var menu = buildMenu("DmgTypeFX Configuration Menu", list, mainNavBlock);
var instructions = "<p style='font-size:10pt; text-align:center;'>This API monitors chat for damage type keywords and triggers FX when tokens lose HP (damage) or gain HP (heal). Damage FX use the last message in chat to determine types; Heal uses a dedicated FX mapping (default: glow-slime). Use '!DmgTypeFX-menu' to open this configuration menu. Click a type to choose Shape and Color from preset drop-downs.</p>";
sendChat("DmgTypeFX", "/w gm " + instructions + menu);
}
});
// Help menu (GM whisper)
on("chat:message", function(msg) {
if (msg.type !== "api") return;
var args = msg.content.split(/\s+/);
if (args[0] === "!DmgTypeFX-help") {
var helpText = "<p style='font-size:10pt; text-align:left;'>" +
"Instructions:<br>" +
"• The script monitors chat for damage type keywords (e.g., fire, acid).<br>" +
"• On HP decrease (bar1): Triggers FX for the most recent chat’s damage types.<br>" +
"• On HP increase (bar1): Triggers the HEAL FX (configurable; default is glow-slime).<br>" +
"• Use '!DmgTypeFX-menu' to open the configuration menu (GM only). Click a type (including 'heal') to set Shape/Color via preset drop-downs.<br>" +
"• TokenMod is supported via '--set bar1_value|±N' for both damage and healing." +
"</p>";
var menu = buildMenu("DmgTypeFX Help", helpText, helpNavBlock);
sendChat("DmgTypeFX", "/w gm " + menu);
}
});
// Config command
on("chat:message", function(msg) {
if (msg.type !== "api") return;
var args = msg.content.split(/\s+/);
if (args[0] === "!DmgTypeFX-config") {
if (args.length < 4) {
sendChat("DmgTypeFX", "/w gm Usage: !DmgTypeFX-config [DamageType|heal] [Shape] [Color]");
return;
}
var dmgType = (args[1] || "").toLowerCase();
var shape = args[2] || "";
var color = args[3] || "";
var newFx = shape.toLowerCase() + "-" + color.toLowerCase();
damageFxMap[dmgType] = newFx;
sendChat("DmgTypeFX", "/w gm " + dmgType + " FX updated to: " + newFx);
}
});
log('DmgTypeFX v1.2 | Ready (includes heal FX).');
// Expose hooks
return {
TriggerFx: triggerFxForToken,
TriggerHealFx: triggerHealFxForToken
};
})();