Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

api or macro to make token "flash"

I'm looking for a way to make tokens flash, kind of like the Bounce API but instead of bouncing the token would emit light for a given tiem with a given interval. had a look at the api list and can't see anything obvious that can do this. anyone got a suggestion? Thanks
1643563687
The Aaron
Roll20 Production Team
API Scripter
Hmm. Interesting.  Can you give more details about how you'd like it to flash?  One problem is the API changing UDL is a bit broken right now, so it might not work well.
well in this specific instance i have a an enemy regularly teleporting some minions on my map which i want to intermittently emit light, like for 1 second every 5 seconds or something to denote the spell happening so that as my players explore the map they have a chance to notice that something is happening there. but in general something like the bounce api to highlight a specific token using flashing light would eb cool for several reasons. I thought about using token-mod with some kind of delay but it instantly got beyond me and thought i'd consult the elder brain that is the forums
1643644758

Edited 1643732170
The Aaron
Roll20 Production Team
API Scripter
Something like this? Toggle with: !toggle-flashing Note that you need to be fast to toggle them off, when the API makes changes to a token, it deselects it in the UI.  If that becomes a problem, I can code up a solution. You can change up the settings at the top of the script.  CHANGE_MIN and CHANGE_MAX are the minimum and maximum time between changes.  If they are the same, all the selected tokens will flash in sequence, if they're farther apart, you'll get a more random look.  EmitPattern is the bright and dim light settings it makes every PHASE_DELAY ms (100ms currently), which is what gives it the pinging look. Code: on('ready',()=>{ const scriptName = 'RandomLightFlash'; const version = '0.1.0'; const schemaVersion = 0.1; const lastUpdate = 1643644316; const CHANGE_MIN = 5; const CHANGE_MAX = 7; const EmitPattern = [[0,2.5],[2.5,5],[5,10],[5,5],[2.5,5],[0,2.5]]; const PHASE_DELAY = 100; const checkInstall = () => { log(`-=> ${scriptName} v${version} <=- [${lastUpdate}]`); if ( !state.hasOwnProperty(scriptName) || state[scriptName].version !== schemaVersion ) { log(` > Updating Schema to v${schemaVersion} <`); switch (state[scriptName] && state[scriptName].version) { case 0.1: /* break; // intentional dropthrough */ /* falls through */ case "UpdateSchemaVersion": state[scriptName].version = schemaVersion; break; default: state[scriptName] = { version: schemaVersion, options: {}, tokenMap: {} }; break; } } }; const flashToken = (t) => { t.set({ emits_bright_light: true, emits_low_light: true, }); let phases = [...EmitPattern]; const burndown = () => { let phase = phases.shift(); if(phase){ t.set({ bright_light_distance: phase[0], low_light_distance: phase[0]+phase[1] }); setTimeout(burndown,PHASE_DELAY); } else { t.set({ emits_bright_light: false, emits_low_light: false, bright_light_distance: 0, low_light_distance: 0 }); } }; burndown(); }; const checkTokens = () => { let now = Date.now(); Object.keys(state[scriptName].tokenMap).forEach(t=>{ if(state[scriptName].tokenMap[t].nextChange < now){ let token = getObj('graphic',t); if(token){ flashToken(token); state[scriptName].tokenMap[t].nextChange = now + (CHANGE_MIN + randomInteger(CHANGE_MAX-CHANGE_MIN))*1000; } else { delete state[scriptName].tokenMap[t]; } } }); }; let interval = setInterval(checkTokens,1000); on('chat:message',msg=>{ if('api'===msg.type && /^!toggle-flashing(\b\s|$)/i.test(msg.content) && playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); let now = Date.now(); (msg.selected || []) .map(o=>getObj('graphic',o._id)) .filter(g=>undefined !== g) .forEach(token=>{ if(state[scriptName].tokenMap.hasOwnProperty(token.id)){ delete state[scriptName].tokenMap[token.id]; } else { state[scriptName].tokenMap[token.id] = { nextChange: now + (CHANGE_MIN + randomInteger(CHANGE_MAX-CHANGE_MIN))*1000 }; } }) ; } }); checkInstall(); });
The Aaron said: Something like this? Toggle with: !toggle-flashing Note that you need to be fast to toggle them off, when the API makes changes to a token, it deselects it in the UI.  If that becomes a problem, I can code up a solution. You can change up the settings at the top of the script.  CHANGE_MIN and CHANGE_MAX are the minimum and maximum time between changes.  If they are the same, all the selected tokens will flash in sequence, if they're farther apart, you'll get a more random look.  EmitPattern is the bright and dim light settings it makes every PHASE_DELAY ms (100ms currently), which is what gives it the pinging look. Code: on('ready',()=>{ const scriptName = 'RandomLightFlash'; const version = '0.1.0'; const schemaVersion = 0.1; const lastUpdate = 1643644316; const CHANGE_MIN = 5; const CHANGE_MAX = 7; const EmitPattern = [[0,2.5],[2.5,5],[5,10],[5,5],[2.5,5],[0,2.5]]; const PHASE_DELAY = 100; const checkInstall = () => { log(`-=> ${scriptName} v${version} <=- [${lastUpdate}]`); if ( !state.hasOwnProperty(scriptName) || state[scriptName].version !== schemaVersion ) { log(` > Updating Schema to v${schemaVersion} <`); switch (state[scriptName] && state[scriptName].version) { case 0.1: /* break; // intentional dropthrough */ /* falls through */ case "UpdateSchemaVersion": state[scriptName].version = schemaVersion; break; default: state[scriptName] = { version: schemaVersion, options: {}, tokenMap: {} }; break; } } }; const flashToken = (t) => { t.set({ emits_bright_light: true, emits_low_light: true, }); let phases = [...EmitPattern]; const burndown = () => { let phase = phases.shift(); if(phase){ t.set({ bright_light_distance: phase[0], low_light_distance: phase[0]+phase[1] }); setTimeout(burndown,PHASE_DELAY); } else { t.set({ emits_bright_light: false, emits_low_light: false, bright_light_distance: 0, low_light_distance: 0 }); } }; burndown(); }; const checkTokens = () => { let now = Date.now(); Object.keys(state[scriptName].tokenMap).forEach(t=>{ if(state[scriptName].tokenMap[t].nextChange < now){ let token = getObj('graphic',t); if(token){ flashToken(token); state[scriptName].tokenMap[t].nextChange = now + (CHANGE_MIN + randomInteger(CHANGE_MAX-CHANGE_MIN))*1000; } else { delete state[scriptName].tokenMap[t]; } } }); }; let interval = setInterval(checkTokens,1000); on('chat:message',msg=>{ if('api'===msg.type && /^!toggle-flashing(\b\s|$)/i.test(msg.content) && playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); let now = Date.now(); (msg.selected || []) .map(o=>getObj('graphic',o._id)) .filter(g=>undefined !== g) .forEach(token=>{ if(state[scriptName].tokenMap.hasOwnProperty(token.id)){ delete state[scriptName].tokenMap[token.id]; } else if(token.get('sides').length) { state[scriptName].tokenMap[token.id] = { nextChange: now + (CHANGE_MIN + randomInteger(CHANGE_MAX-CHANGE_MIN))*1000 }; } }) ; } }); checkInstall(); }); ok this is exactly what i was wanting, unfortunately at the moment it doesn't seem to work, i'm sure it's my fault: it only seems to work on one specific token i use, it's a token called lightsource that i made, it's a multi sided token which can look like several different kinds of lightsource, campfire/torch/brazier/etc. the images are ones i created it has a character sheet with a few basic macros that use token-mod to witch between a few lighting options quickly, it isn't attached to any character or in the player journal, i have tried using other tokens, some with character sheets attached some without, some I've made and also ones from roll20, I've tried using the images that make up the lightsource token but they don't work either, only specifically the lightsource token seems to work, i can't figure out whats special about it compared to the others
ok after further experimentation it seems to only work on rollable table tokens, maybe?
1643732307
The Aaron
Roll20 Production Team
API Scripter
Oh SNAP!  You're right! I fixed the source above.  I had used a script that randomly rotated the faces of a rollable table token as a base and left one check in there for a number of sides.  Ironic that the tokens I was testing with were all rollable table tokens.  Something for me to be careful about in the future.
The Aaron said: Oh SNAP!  You're right! I fixed the source above.  I had used a script that randomly rotated the faces of a rollable table token as a base and left one check in there for a number of sides.  Ironic that the tokens I was testing with were all rollable table tokens.  Something for me to be careful about in the future. thanks for this anyway man it's fantastic