This has probably been done before, but here's a fun little script to add lightning effects to a map. We don't use sound in our games, so no soundFX in the current script, but maybe later. EDIT - Note: currently only supports Legacy Dynamic Lighting Click for animated gif of our hero about to realize he's in a dire situation (11s loop) Same FX settings with DL layer trees (7s loop). Setup: 1) Place a handful of tokens across the map with the following settings: name : "Lightning" All Players See Light : checked image: transparent png Aura1: 0ft (so the GM can see the square of the source) 2) Call !lightning on (+optional params?) to start the light show 3) Call !Lightning off to stop. Only lightning tokens on the player page are affected Optional parameters in bold below: !lightning <on/off> <minInterval> <maxInterval> <maxDuration>
minInterval = minimum time between flashes (in milliseconds) -- DEFAULT = 200
minInterval = maximum time between flashes (in milliseconds) -- DEFAULT = 10000
maxDuration = maximum duration of each flash (in milliseconds) -- DEFAULT = 400 Examples of valid calls: !lightning on //uses default values !lightning off //turns off FX (there may be one more cycle that has to finish after command is given) !lightning on 400 //overrides the minInterval, rest are default values !lightning on 400 8000 //overrides the min & max Intervals, with default value used for maxDuration !lightning on 400 8000 200 //overrides all timer values For the animated gif example above, I used 5 lightning tokens (red highlight) and drew tree trunks in the DL layer. I thought it created a pretty spooky effect. Here's the script! const LightningFX = (() => {
const scriptName = "LightningFX";
const version = '0.1';
var globalKill = true;
const LIGHT_NAME = 'Lightning';
const checkInstall = function() {
log(scriptName + ' v' + version + ' initialized.');
};
const flashOff = function(src) {
src.set('light_radius', 0);
}
const flashOn = function(src, dim, timeout) {
let rand = Math.random() * timeout;
if (dim) {
src.set('light_dimradius', -15);
src.set('light_radius', 200);
} else {
src.set('light_radius', 200);
}
setTimeout(flashOff, rand, src);
}
const handleFX = function(sources, minInterval, maxInterval, maxDuration, active) {
let dim = false;
if (active) {
let rand = Math.floor(Math.random() * (maxInterval - minInterval + 1) + minInterval);
if ( randomInteger(2) === 2 ) {
dim = true;
} else {
dim = false;
}
setTimeout(function() {
sources.forEach((src) => {
flashOn(src, dim, maxDuration);
});
if (!globalKill) { handleFX(sources, minInterval, maxInterval, maxDuration, true); }
}, rand);
} else {
sources.forEach((src) => {
flashOff(src);
});
}
}
const handleInput = function(msg) {
//set defaults
let minInterval = 200;
let maxInterval = 10000;
let maxDuration = 400;
try {
if(msg.type=="api" && msg.content.indexOf("!lightning") === 0 && playerIsGM(msg.playerid)) {
who = getObj('player',msg.playerid).get('_displayname');
let cmd = msg.content.split((/\s+/));
cmd.shift();
switch (cmd.length) {
case 0:
//no commands, do nothing
sendChat('Lightning',`/w "${who}" `+ 'No commands given to !Lightning script');
break;
case 1:
//on or off
break;
case 2:
//minInterval
minInterval = parseInt(cmd[1])
break;
case 3:
//minInterval maxInterval
minInterval = parseInt(cmd[1])
maxInterval = parseInt(cmd[2])
break;
case 4:
default:
//minInterval maxInterval maxDuration
minInterval = parseInt(cmd[1])
maxInterval = parseInt(cmd[2])
maxDuration = parseInt(cmd[3])
break;
}
if ( isNaN(minInterval) || isNaN(maxInterval) || isNaN(maxDuration) ) {
sendChat('Lightning',`/w "${who}" `+ 'Error. Non-Numeric timer settings detected');
return;
}
var sources = findObjs({
_pageid: Campaign().get("playerpageid"),
_type: "graphic",
name: LIGHT_NAME
});
if (!sources) {
sendChat('Lightning',`/w "${who}" `+ 'No Lightning Sources on Player Page!');
globalKill = true;
return;
}
switch (cmd[0].toLowerCase()) {
case "on":
//TURN ON FX
globalKill = false;
handleFX(sources, minInterval, maxInterval, maxDuration, true);
break;
case "off":
//TURN OFF FX
globalKill = true;
handleFX(sources, minInterval, maxInterval, maxDuration, false);
break;
default:
sendChat('Lightning',`/w "${who}" `+ 'Unrecognized command. Expected Lighting \"on\" or \"off\"');
return;
}
}
}
catch(err) {
sendChat('Lightning',`/w "${who}" `+ 'Unhandled exception: ' + err.message);
}
};
const registerEventHandlers = function() {
on('chat:message', handleInput);
};
on("ready",() => {
checkInstall();
registerEventHandlers();
});
})();