
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(); }); })();