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