// Github: https://github.com/shdwjk/Roll20API/blob/master/BounceTokens/BounceTokens.js // By: The Aaron, Arcane Scriptomancer // Contact: https://app.roll20.net/users/104025/the-aaron var API_Meta = API_Meta||{}; API_Meta.BounceTokens={offset:Number.MAX_SAFE_INTEGER,lineCount:-1}; {try{throw new Error('');}catch(e){API_Meta.BounceTokens.offset=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-6);}} var BounceTokens = BounceTokens || (function(){ 'use strict'; var version = '0.1.3'; API_Meta.BounceTokens.version = version; var lastUpdate = 1609295654, schemaVersion = 0.1, stepRate = 200, defaultSecondsPerCycle = 20, millisecondsPerSecond = 1000, ch = function (c) { var entities = { '<' : 'lt', '>' : 'gt', "'" : '#39', '@' : '#64', '{' : '#123', '|' : '#124', '}' : '#125', '[' : '#91', ']' : '#93', '"' : 'quot', '-' : 'mdash', ' ' : 'nbsp' }; if(_.has(entities,c) ){ return ('&'+entities[c]+';'); } return ''; }, showHelp = function() { sendChat('', '/w gm '+ '
'+ '
'+ 'BounceTokens v'+version+ '
'+ '
'+ '
'+ '

Allows the GM to toggle bouncing of selected tokens

'+ '
'+ 'Commands'+ '
!bounce-start '+ch('[')+'Seconds Per Cycle'+ch(']')+' '+ch('[')+'-- tokenID ...'+ch(']')+''+ '
'+ 'Starts a selected or specified tokens bouncing, optionally with a speed.'+ '
    '+ '
  • '+ 'Seconds Per Cycle '+ch('-')+' Specifies the number of seconds for the token to make a full bounce. Default: '+defaultSecondsPerCycle +'
  • '+ ' '+ '
'+ '
'+ '
'+ '
!bounce-stop '+ch('[')+'-- tokenID ...'+ch(']')+''+ '
'+ 'Stops the selected or specified tokens from bouncing.'+ '
'+ '
'+ '
' ); }, handleInput = function(msg) { if ( "api" !== msg.type || !playerIsGM(msg.playerid) ) { return; } let parts = msg.content.split(/\s+--\s+/); let args = parts[0].split(/\s+/); let ids = (parts[1]||'').split(/\s+/).filter((id)=>id.length); switch(args[0]) { case '!bounce-start': { if(!( msg.selected && msg.selected.length > 0 ) && ids.length===0) { showHelp(); return; } let secondsPerCycle = Math.abs(args[1] || defaultSecondsPerCycle); ids=[...new Set([...(msg.selected||[]).map((s)=>s._id), ...ids])]; ids.map((id)=>getObj('graphic',id)) .filter((o)=>undefined !== o) .forEach((o)=>{ state.BounceTokens.bouncers[o.id]={ id: o.id, top: o.get('top'), page: o.get('pageid'), rate: (secondsPerCycle*millisecondsPerSecond) }; }) ; } break; case '!bounce-stop': { if(!( msg.selected && msg.selected.length > 0 ) && ids.length===0) { showHelp(); return; } ids=[...new Set([...(msg.selected||[]).map((s)=>s._id), ...ids])]; ids.map((id)=>getObj('graphic',id)) .filter((o)=>undefined !== o) .filter((o)=>state.BounceTokens.bouncers.hasOwnProperty(o.id)) .forEach((o)=>{ o.set('top',state.BounceTokens.bouncers[o.id].top); delete state.BounceTokens.bouncers[o.id]; }) ; } break; } }, getActivePages = () => _.union([ Campaign().get('playerpageid')], _.values(Campaign().get('playerspecificpages')), _.chain(findObjs({ type: 'player', online: true })) .filter((p)=>playerIsGM(p.id)) .map((p)=>p.get('lastpage')) .value() ), animateBounce = function() { var pages = getActivePages(); _.chain(state.BounceTokens.bouncers) .filter(function(o){ return _.contains(pages,o.page); }) .each(function(sdata){ var s = getObj('graphic',sdata.id); if(!s) { delete state.BounceTokens.bouncers[sdata.id]; } else { s.set({ top: state.BounceTokens.bouncers[sdata.id].top - (s.get('height')*0.25)*Math.sin(( (Date.now()%sdata.rate)/sdata.rate )*Math.PI) }); } }); }, handleTokenDelete = function(obj) { var found = _.findWhere(state.BounceTokens.bouncers, {id: obj.id}); if(found) { delete state.BounceTokens.bouncers[obj.id]; } }, handleTokenChange = function(obj) { var found = _.findWhere(state.BounceTokens.bouncers, {id: obj.id}); if(found) { state.BounceTokens.bouncers[obj.id].top= obj.get('top'); } }, checkInstall = function() { log('-=> BounceTokens v'+version+' <=- ['+(new Date(lastUpdate*1000))+']'); if( ! _.has(state,'BounceTokens') || state.BounceTokens.version !== schemaVersion) { log(' > Updating Schema to v'+schemaVersion+' <'); state.BounceTokens = { version: schemaVersion, bouncers: {} }; } setInterval(animateBounce,stepRate); }, registerEventHandlers = function() { on('chat:message', handleInput); on('destroy:graphic', handleTokenDelete); on('change:graphic', handleTokenChange); }; return { CheckInstall: checkInstall, RegisterEventHandlers: registerEventHandlers }; }()); on("ready",function(){ 'use strict'; BounceTokens.CheckInstall(); BounceTokens.RegisterEventHandlers(); }); {try{throw new Error('');}catch(e){API_Meta.BounceTokens.lineCount=(parseInt(e.stack.split(/\n/)[1].replace(/^.*:(\d+):.*$/,'$1'),10)-API_Meta.BounceTokens.offset);}}