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

Is there a way to tell which Mod (API) Scripts work in Jumpagate and which don't?

Hi Is there a way to tell which Mods work in Jumpgate and which don't? I mean, without having to test each one manually? I use over 40 in the game I run. Maybe there is a list of which ones are compatible?  In a Jumpgate campaign, when you try to install a script, will it hide incompatible scripts from the list or warn you in any way? I'd be interested in moving my D&D 5e game to Jumpgate but I wouldn't be willing to to so if some of my favorite scripts wouldn't work. Some help speed things up while some others help with immersion. Any tips are appreciated. Thanks --- Here are most of the scripts I currently use: 5th Edition OGL by Roll20 Companion autoPing.js apply-damage.js Bloodied-Dead-Maker.js ChatSetAttr Check It Out Character Sheet Utils Concentration Customizable Roll Listener D&D 5e - Paladin Aura door-sounds.js GroupCheck GroupInitiative HiddenCheck.js HTML Builder linTokenMarkers libline Mapchange Marching Order MatrixMath Messenger multiTokenFX.js Path Math pingme.js RecursiveTable.js Roll20 Audio Master Rollable Table Macros ScriptCards SelectManager Simple Sound SmartAOE splitArgs TableExport Teleport Token Action Marker Token Collisions TokenMarker Token Fate TokenMod Turnmarker1.js Vector Math whisperart.js
1734202838

Edited 1734205967
The Aaron
Roll20 Production Team
API Scripter
As of right now, most of those should work, which a few exceptions: sendPing() is not working currently (though it is being actively worked on!), so pingme and autoping aren't going to be useful (though they won't crash or anything, just won't do the pinging).  Once the ping functionality is fixed, they will just start working as normal.  Scripts that have ping as part of them will similarly have that functionality missing until the function is fixed (TurnMarker1, possibly Teleport) Scripts that make use of player created paths will need to be updated to support Pathv2.  I've updated PathMath, Token Collisions, and It's a Trap for this already, which might cover all of what you're using. There is a bug (not strictly related to the API) that is being looked at right now which happens when using @{target|...} and @{selected|...}.  That may affect some scripts that generate buttons which use those for supplying arguments.  I don't know which of your scripts might do that, it's really more of a macros/chat issue. Scripts that make use of multiple of the same status marker (Flight and Flymore, FateDots, some TokenMod commands) won't show more than one of them.  This has been brought up, I'll follow up on it with the devs. That covers Jumpgate and API Scripts.  If you are using the 2024 sheet, there are some other things to be aware of, primarily that no scripts currently can interact with it.
Thanks for the update The Aaron, i was wondering how the scripting for Jumpgate was coming along. Much appreciated :)
1734211553
Gold
Forum Champion
Will this paths change affect the snippit script, "Waypoint Patrol"? The paths are GM created, not "player created" per-se, if that helps. It is a very old script from the Forums that the script author hasn't been around here in a while.  So can I still use Waypoint Patrol normally in Jumpgate?  The Aaron said: Scripts that make use of player created paths will need to be updated to support Pathv2.  I've updated PathMath, Token Collisions, and It's a Trap for this already, which might cover all of what you're using.
1734212190
The Aaron
Roll20 Production Team
API Scripter
Anything using the user interface to draw paths on Jumpgate and then look at those paths in scripts will need to be updated. Anything where an API script is creating a path using the classic path object will continue work as expected. For example, UniversalVTTImporter will still work just fine, though I am going to be updating it to PathV2 objects. Player in this sense means a human rather than a script. (All humans are "players" to us API scripts! =D )
1734213830
Gold
Forum Champion
Ok thanks for the info. I'm going to be needing some help (or recruiting a volunteer) to update Waypoint Patrol script snippit to work with PathV2 for Jumpgate. state.WaypointPatrol = state.WaypointPatrol || {'PatrolStatus': true} var WPP = {}; WPP.Command = '!wp'; WPP.SpeakAs = 'Patrol'; WPP.RefreshRate = 5; // in seconds, minimum 1 WPP.RetainLastMove = true; //save patrol moves as lastmove WPP.ShowPaths = true; //refresh visible paths on add or delete WPP.AddPatrolPath = function(selected, patrolname) { //make sure the patrol name is provided if (!patrolname || patrolname.trim() === '' ) { return 'Err: patrol name missing'; } patrolname = patrolname.toLowerCase(); //verify only one token is selected if (!selected) { return 'Err: no token selected'; } if (selected.length !== 1) { return 'Err: multiple tokens selected'; } if (!selected[0]['_type'] || selected[0]['_type'] !== 'graphic') { return 'Err: selected must be a graphic'; } //get the token object var token = getObj('graphic', selected[0]['_id']); if (!token) { return 'Err: token not found'; } //read the patrol path from token's last move var lastmove = token.get('lastmove'); if (lastmove === '') { return 'Err: token missing last move'; } lastmove = lastmove.split(','); var pts = []; while(lastmove.length > 0) { pts.push({ 'left': +lastmove.shift(), 'top' : +lastmove.shift(), }); } pts.push({ 'left': token.get('left'), 'top' : token.get('top'), }); //add patrol path to state if it doesn't already exist var pageid = token.get('_pageid'); if (!state.WaypointPatrol['paths']) { state.WaypointPatrol['paths'] = {}; } if (!state.WaypointPatrol['paths'][pageid]) { state.WaypointPatrol['paths'][pageid] = {}; } if (state.WaypointPatrol['paths'][pageid][patrolname]) { return 'Err: name already exists'; } state.WaypointPatrol['paths'][pageid][patrolname] = pts; if (WPP.ShowPaths) { WPP.ShowPatrolPaths(); } WPP.PatrolHelpMenu(); return 'Success: patrol path added'; } WPP.DeletePatrolPath = function(patrolname) { //make sure the patrol name is provided if (!patrolname || patrolname.trim() === '' ) { return 'Err: patrol name missing'; } patrolname = patrolname.toLowerCase(); //delete the patrol name if it exists var pageid = Campaign().get('playerpageid'); if (!state.WaypointPatrol['paths'][pageid] || !state.WaypointPatrol['paths'][pageid][patrolname]) { return 'Err: patrol name does not exist for this map'; } delete state.WaypointPatrol['paths'][pageid][patrolname]; if (WPP.ShowPaths) { WPP.ShowPatrolPaths(); } WPP.PatrolHelpMenu(); return 'Success: patrol path deleted.'; } WPP.PatrolHelpMenu = function() { //create a bracketed list of avaialble patrols for the current map var pageid = Campaign().get('playerpageid'); var paths = []; if (state.WaypointPatrol['paths'] && state.WaypointPatrol['paths'][pageid] && _.size(state.WaypointPatrol['paths'][pageid]) > 0) { paths = _.keys(state.WaypointPatrol['paths'][pageid]); } var code = encodeURIComponent(':'); var out = ''; out = '<div style="background-color: #FFF; border: 1px solid #000; border-radius: 0.5em; margin-left: 2px; margin-right: 2px; padding-top: 5px; padding-bottom: 5px;">' + '<div style="font-weight: bold; text-align: center; border-bottom: 1px solid black; font-size: 100%">Map: ' + (getObj('page', pageid).get('name') || '(Untitled)') + '</div>' + '<div style="padding-left: 5px; padding-right: 5px; overflow: hidden; font-size: 90%">'; if (paths.length > 0) { out += '<div style="font-weight: bold; padding-top: 5px; padding-bottom: 5px;">Create a Patrol<a style="float: right; padding: 0px 5px 0px 5px; margin: 0px 5px 0px 5px; border: 1px solid black; border-radius: 0.5em; background-color: #999999; color: #FFF" href="' + WPP.Command + ' patrol path' + code +'?{Name of the path|'+ paths.join('|') + '} mode' + code + '?{Mode|Forward and Backwards,0|Forward Only,1|Backwards Only,-1} speed' + code + '?{Distance moved on each refresh|5} rotation' + code + '?{Angle modifier to face waypoint|Do Not Rotate,false|0 degrees,0|90 degrees,90|180 degrees,180|270 degrees,270}">Patrol</a></div>'; out += '<li style="padding-left: 10px;">Starts moving a token graphic along an existing patrol path.</li>'; } else { out += '<div style="padding-top: 5px; padding-bottom: 5px;">You must add a path to this map from a token\'s last move before creating a patrol.</div>'; out += '<div style="padding-top: 5px; padding-bottom: 5px;">Place a token to where you want the patrol path to begin. Move the token to where you want the patrol path to end. You can press the space bar to create turns in the path as you\'re moving the token. When the token is in the final position, click the \'Path\' button</div>' } out += '<div style="font-weight: bold; padding-top: 5px; padding-bottom: 5px;">Create a Path<a style="float: right; padding: 0px 5px 0px 5px; margin: 0px 5px 0px 5px; border: 1px solid black; border-radius: 0.5em; background-color: #999999; color: #FFF" href="' + WPP.Command + ' addpath ?{Name of the new path}">Path</a></div>'; out += '<li style="padding-left: 10px;">Saves a token\'s last move as a named patrol path for other tokens to follow.</li>'; if (paths.length > 0){ out += '<div style="font-weight: bold; padding-top: 5px; padding-bottom: 5px;">Delete a Path<a style="float: right; padding: 0px 5px 0px 5px; margin: 0px 5px 0px 5px; border: 1px solid black; border-radius: 0.5em; background-color: #999999; color: #FFF" href="' + WPP.Command + ' deletepath ?{Name of existing path to delete|' + paths.join('|') + '}">Delete</a></div>'; out += '<li style="padding-left: 10px;">Deletes an existing patrol path.</li>'; out += '<div style="font-weight: bold; padding-top: 5px; padding-bottom: 5px;">Show All Paths<a style="float: right; padding: 0px 5px 0px 5px; margin: 0px 5px 0px 5px; border: 1px solid black; border-radius: 0.5em; background-color: #999999; color: #FFF" href="' + WPP.Command + ' showpaths">Show</a></div>'; out += '<li style="padding-left: 10px;">Draws onto the GM layer all existing patrol paths and labels them.</li>'; out += '<div style="font-weight: bold; padding-top: 5px; padding-bottom: 5px;">Hide All Paths<a style="float: right; padding: 0px 5px 0px 5px; margin: 0px 5px 0px 5px; border: 1px solid black; border-radius: 0.5em; background-color: #999999; color: #FFF" href="' + WPP.Command + ' hidepaths">Hide</a></div>'; out += '<li style="padding-left: 10px;">Hides all drawn paths and labels.</li>'; } out += '</div></div>'; sendChat(WPP.SpeakAs, '/w GM ' + out); return; } WPP.ShowPatrolPaths = function() { WPP.HidePatrolPaths(); if (!state.WaypointPatrol['paths']) { return; } state.WaypointPatrol['traces'] = state.WaypointPatrol['traces'] || []; state.WaypointPatrol['labels'] = state.WaypointPatrol['labels'] || []; for(pageid in state.WaypointPatrol['paths']) { for(patrolname in state.WaypointPatrol['paths'][pageid]) { var pts = []; var ul = {'left': Infinity, 'top': Infinity}; var lr = {'left': 0, 'top': 0}; for (var pt = 0; pt < state.WaypointPatrol['paths'][pageid][patrolname].length ; pt++) { var curr = state.WaypointPatrol['paths'][pageid][patrolname][pt]; ul = {'left': Math.min(ul['left'],curr['left']), 'top': Math.min(ul['top'], curr['top'])}; lr = {'left': Math.max(lr['left'],curr['left']), 'top': Math.max(lr['top'], curr['top'])}; pts.push(['L', curr['left'], curr['top']]); }; state.WaypointPatrol['traces'].push(createObj('path', { '_pageid' : pageid, '_path' : JSON.stringify(pts), //see notes 'stroke' : '#ff9900', 'stroke_width': 14, 'layer' : 'gmlayer', 'width' : lr['left'] - ul['left'], 'height' : lr['top'] - ul['top'], 'left' : (lr['left'] - ul['left']) / 2 + ul['left'], 'top' : (lr['top'] - ul['top']) / 2 + ul['top'], }).get('_id')); for ( var pt = 0; pt < state.WaypointPatrol['paths'][pageid][patrolname].length - 1; pt++) { var p1 = state.WaypointPatrol['paths'][pageid][patrolname][pt]; var p2 = state.WaypointPatrol['paths'][pageid][patrolname][pt + 1]; state.WaypointPatrol['labels'].push(createObj('text', { '_pageid' : pageid, 'text' : 'path: ' + patrolname, 'font_size' : 14, 'color' : 'rgb(0,0,0)', 'font_family': 'Arial', 'layer' : 'gmlayer', 'left' : Math.abs(p1['left']-p2['left']) / 2 + Math.min(p1['left'],p2['left']), 'top' : Math.abs(p1['top']-p2['top']) / 2 + Math.min(p1['top'],p2['top']), 'rotation' : ((p2['left'] < p1['left']) ? Math.atan2(p1['top'] - p2['top'], p1['left'] - p2['left']) : Math.atan2(p2['top'] - p1['top'], p2['left'] - p1['left'])) * 180 / Math.PI }).get('_id')); } } } WPP.ShowPaths = true; } WPP.HidePatrolPaths = function() { if (state.WaypointPatrol['traces']) { while (state.WaypointPatrol['traces'].length > 0) { var path = getObj('path', state.WaypointPatrol['traces'].shift()); if (path) { path.remove(); } } } if (state.WaypointPatrol['labels']) { while (state.WaypointPatrol['labels'].length > 0) { var text = getObj('text', state.WaypointPatrol['labels'].shift()); if (text) { text.remove(); } } } WPP.ShowPaths = false; } WPP.SetTokenToPatrol = function(selected, opts) { //cehck to see if a token has been selected if (!selected || selected.length === 0) { return 'Err: no token selected'; } if (!state.WaypointPatrol['tokens']) { state.WaypointPatrol['tokens'] = {}; } if (!opts || !opts['patrolname'] || opts['patrolname'].trim() === '' ) { return 'Err: patrol name missing'; } opts['patrolname'] = opts['patrolname'].toLowerCase(); var pageid = getObj(selected[0]['_type'], selected[0]['_id']).get('_pageid'); if (!state.WaypointPatrol['paths'] || !state.WaypointPatrol['paths'][pageid] || !state.WaypointPatrol['paths'][pageid][opts['patrolname']]) { return 'Err: patrol name does not exist for this map'; } var points = state.WaypointPatrol['paths'][pageid][opts['patrolname']]; for (i in selected) { if (selected[i]['_type'] === 'graphic') { state.WaypointPatrol['tokens'][selected[i]['_id']] = { 'patrolname' : opts['patrolname'], 'active' : opts['active'] || true, 'patrolmode' : opts['patrolmode'] || 0, 'movementrate' : opts['movementrate'] || getObj('page', pageid).get('scale_number'), 'rotation' : isNaN(opts['rotation']) ? false : opts['rotation'], 'random' : opts['random'] || false, 'phase' : opts['phase'] || 0, }; //move token to closest point on patrol path var obj = getObj('graphic', selected[i]['_id']); WPP.MoveTokenToPath(obj, points); } } return 'Success: patrol added'; } WPP.ModifyPatrollingToken = function(selected, opts) { if(!selected || selected.length === 0) { return 'Err: no token selected'; } var pageid = getObj(selected[0]['_type'], selected[0]['_id']).get('_pageid'); if('patrolname' in opts && !state.WaypointPatrol['paths'][pageid][opts['patrolname']]) { return 'Err: patrol name does not exist for this map';} if (state.WaypointPatrol['tokens']) { for (o in selected) { if (state.WaypointPatrol['tokens'][selected[o]['_id']]) { for (opt in opts) { if (opts.hasOwnProperty(opt)) { state.WaypointPatrol['tokens'][selected[o]['_id']][opt] = opts[opt]; switch(opt) { case 'patrolname': WPP.MoveTokenToPath(getObj('graphic',selected[o]['_id']), state.WaypointPatrol['paths'][pageid][opts[opt]]); break; } } } } } } return 'Success: patrol modified'; } WPP.RemoveTokenFromPatrol = function(selected) { if (!selected || selected.length === 0) { return 'Err: no token selected'; } if (state.WaypointPatrol['tokens']) { for (o in selected) { if (selected[o]['_type'] == 'graphic') { delete state.WaypointPatrol['tokens'][selected[o]['_id']]; } } } return 'Success: patrols removed'; } WPP.MovePatrols = function() { //loop through each token in state data, cleanup deleted if(!state.WaypointPatrol['tokens']) { return; } for (id in state.WaypointPatrol['tokens']) { //validate object exists var obj = getObj('graphic', id); if (!obj) { delete state.WaypointPatrol['tokens'][id]; continue; } //validate patrol exists var pageid = obj.get('_pageid'); var patrolname = state.WaypointPatrol['tokens'][id]['patrolname']; if (!patrolname || !state.WaypointPatrol['paths'][pageid] || !state.WaypointPatrol['paths'][pageid][patrolname]) { obj.set({'layer':'objects'}); delete state.WaypointPatrol['tokens'][id]; continue; } //only patrol active units if (!state.WaypointPatrol['tokens'][obj.get('_id')]['active']) { continue; } //check for random movement, 20% chance the token doesn't move if (state.WaypointPatrol['tokens'][obj.get('_id')]['random'] && Math.random() < 0.2) { continue; } //set up the proper path var points = state.WaypointPatrol['paths'][pageid][patrolname].slice(0); switch(state.WaypointPatrol['tokens'][id]['patrolmode']) { case 1: //forward patrolling break; case -1: //reverse patrolling points.reverse(); break; default: //back and forth patrolling points = points.concat(points.slice(0).reverse()); break; } //set up control variables var nextindex = state.WaypointPatrol['tokens'][id]['nextpointindex'] > points.length -1 ? 0 : state.WaypointPatrol['tokens'][id]['nextpointindex'] ; var moverate = state.WaypointPatrol['tokens'][id]['movementrate'] <= 0 ? 5 : state.WaypointPatrol['tokens'][id]['movementrate']; var distremain = moverate * 70 / getObj('page', obj.get('_pageid')).get('scale_number'); var newpoint = {'left': obj.get('left'), 'top': obj.get('top')}; var lastmove = [newpoint['left'], newpoint['top']]; //move along the path based on the movement rate of the token do { var disttonext = WPP.Distance(newpoint, points[nextindex]); if (distremain > disttonext) { newpoint = points[nextindex]; lastmove.push(points[nextindex]['left'],points[nextindex]['top']); distremain -= disttonext; nextindex = (nextindex >= points.length - 1) ? 0 : nextindex + 1; } else { var moveratio = distremain / disttonext; var newrotation = (state.WaypointPatrol['tokens'][id]['rotation'] === false) ? obj.get('rotation') : Math.atan2(points[nextindex]['top'] - newpoint['top'], points[nextindex]['left'] - newpoint['left']) * 180 / Math.PI - 90 + state.WaypointPatrol['tokens'][id]['rotation'] ;; newpoint = { 'left': moveratio * (points[nextindex]['left'] - newpoint['left']) + newpoint['left'], 'top' : moveratio * (points[nextindex]['top'] - newpoint['top']) + newpoint['top'], }; obj.set({ 'left' : newpoint['left'], 'top' : newpoint['top'], 'lastmove' : WPP.RetainLastMove ? lastmove.join(',') : '', 'layer' : Math.random() < state.WaypointPatrol['tokens'][id]['phase'] ? 'gmlayer' : 'objects' , 'rotation' : newrotation, }); state.WaypointPatrol['tokens'][id]['nextpointindex'] = nextindex; distremain = 0; } } while (distremain > 0); } } WPP.Resume = function(selected) { if(!selected || !state.WaypointPatrol['tokens']) { return; } for (id in selected) { if (state.WaypointPatrol['tokens'][selected[id]['_id']]) { //set the token patrol status to active state.WaypointPatrol['tokens'][selected[id]['_id']]['active'] = true; //move token to the closest point on path var obj = getObj('graphic', selected[id]['_id']); WPP.MoveTokenToPath(obj, state.WaypointPatrol['paths'][obj.get('_pageid')][state.WaypointPatrol['tokens'][selected[id]['_id']]['patrolname']]); } } } WPP.Distance = function(p1, p2) { return Math.sqrt(Math.pow((p1['left'] - p2['left']),2) + Math.pow((p1['top'] - p2['top']),2)); } WPP.MoveTokenToPath = function(obj, path) { //set up control variables var pt = {'left': obj.get('left'), 'top': obj.get('top')}; var newpt = pt; var nextid = 0; var delta = {'left': 0, 'top': 0}; var dmin = Infinity; //loop through each path segment and find the closest point for( var i = 0; i < path.length-1; i++) { //get the next line segment in the path var p1 = path[i]; var p2 = path[i+1]; //calculate the closest point on the segment to the token var delta = {'left': p2['left'] - p1['left'], 'top': p2['top'] - p1['top']}; var u = ((pt['left'] - p1['left']) * delta['left'] + (pt['top'] - p1['top']) * delta['top']) / (Math.pow(delta['left'], 2) + Math.pow(delta['top'], 2)); u = ( u > 1 ) ? 1 : (u < 0) ? 0 : u; var testpt = {'left': p1['left'] + u * delta['left'], 'top': p1['top'] + u * delta['top']}; var d = WPP.Distance(testpt, pt); //save point if it's the closest point of all segments if (d < dmin) { dmin = d; nextid = i + 1; newpt = JSON.parse(JSON.stringify(testpt)); //deep copy of obj } } //move token to closest point obj.set({ 'left' : newpt['left'], 'top' : newpt['top'], 'lastmove' : obj.get('left') + ',' + obj.get('top'), }); //set the next index patrol point for token state.WaypointPatrol['tokens'][obj.get('_id')]['nextpointindex'] = nextid; } on('chat:message', function(msg) { var params = msg.content.split(' '); if (msg.type !== 'api' || !playerIsGM(msg.playerid) || params.shift().toLowerCase() !== WPP.Command.toLowerCase() || params.length === 0) { return; } var replyTo = '/w ' + msg.who.split(' ')[0] + ' '; var cmd = params.shift().toLowerCase(); var out = undefined; //set default parameters var options = {}; function ReadParam(key) { keys = { 'path' : function(val) { options['patrolname'] = val; }, 'random' : function(val) { options['random'] = (val.toLowerCase() === 'true'); }, 'phase' : function(val) { options['phase'] = isNaN(val) ? 0 : Math.min(Math.max(0,val),100) / 100; }, 'active' : function(val) { options['active'] = (val.toLowerCase() === 'true'); }, 'mode' : function(val) { options['patrolmode'] = (val =='1' || val =='-1') ? parseInt(val) : 0; }, 'speed' : function(val) { options['movementrate'] = (isNaN(val) || val < 1) ? 1 : parseFloat(val); }, 'rotation' : function(val) { options['rotation'] = isNaN(val) ? false : parseInt(val % 360); }, 'default' : function() { out = 'Err: Unknown parameter [' + key + ']'; }, }; return (keys[key.toLowerCase()] || keys['default']); } while (params.length > 0) { var opt = decodeURIComponent(params.shift()).split(':'); switch (opt.length) { case 1: if (options['patrolname']) { options['patrolname'] += ' ' + opt[0]; } else { options['patrolname'] = opt[0]; } break; case 2: ReadParam(opt[0])(opt[1]); break; default: out = 'Err: too many delimiters'; break; } } //exit if parameters are invalid if (out) { sendChat(WPP.SpeakAs, replyTo + out); return; } switch(cmd) { case 'on': case 'off': state.WaypointPatrol['PatrolStatus'] = (cmd === 'on'); out = 'Patrolling ' + cmd.toUpperCase(); break; case 'resume': WPP.Resume(msg.selected); break; case 'help': case 'menu': out = WPP.PatrolHelpMenu(); //ListPatrolPaths(); break; case 'addpath': out = WPP.AddPatrolPath(msg.selected, options['patrolname']); break; case 'deletepath': out = WPP.DeletePatrolPath(options['patrolname']); break; case 'patrol': out = WPP.SetTokenToPatrol(msg.selected, options); break; case 'modify': out = WPP.ModifyPatrollingToken(msg.selected, options); break; case 'stoppatrol': out = WPP.RemoveTokenFromPatrol(msg.selected); break; case 'showpaths': WPP.ShowPatrolPaths(); break; case 'hidepaths': WPP.HidePatrolPaths(); break; default: out = 'Err: unknown command [' + cmd + ']'; break; } if (out) { sendChat(WPP.SpeakAs, replyTo + out); } }); on('change:graphic', function(obj, old) { var id = obj.get('_id'); if (!state.WaypointPatrol['tokens'] || !state.WaypointPatrol['tokens'][id] || state.WaypointPatrol['tokens'][id]['active'] === false) return; if (obj.get('left') === old['left'] && obj.get('top') === old['top'] && obj.get('height') === old['height'] && obj.get('width') === old['width'] && obj.get('rotation') === old['rotation']) { return; } state.WaypointPatrol['tokens'][obj.get('_id')]['active'] = false; }); on('ready', function() { setInterval(function() { //do nothing if not enabled or initiative page is open if (!state.WaypointPatrol['PatrolStatus'] || Campaign().get('initiativepage')) { return; } WPP.MovePatrols(); }, Math.max(WPP.RefreshRate,1) * 1000); log('Script loaded: Waypoint Patrols' + (state.WaypointPatrol['PatrolStatus'] ? ', actively patrolling' : '')); });
1734219162
The Aaron
Roll20 Production Team
API Scripter
That doesn't actually use paths, it uses the lastmove of the token, which should work just fine.  If it doesn't, let me know (there was a bug with lastmove on Jumpgate, but it should be fixed at this point).
1734220671
Gold
Forum Champion
Oh, wow. Not even paths afterall!  Yes I will let you know if it doesn't work when I use it with Jumpgate.  tysm
I have asked this before but will ask it again. Instead of needing to use multiple status markers for things like how high you are flying, why not allow for larger numbers on the status markers? You could allow up to 999 and handle this programmatically by allowing a key press delay between numbers, so if you type in 100 quickly show 100 instead of 0. The Aaron said: Scripts that make use of multiple of the same status marker (Flight and Flymore, FateDots, some TokenMod commands) won't show more than one of them.  This has been brought up, I'll follow up on it with the devs.
1734237816
The Aaron
Roll20 Production Team
API Scripter
I certainly like that idea, and I'll mention it, no promises. 
1734261348
Ulti
Pro
Sheet Author
API Scripter
Is there a way for a script to know if it is used in a Jumpgate game?
1734277294
The Aaron
Roll20 Production Team
API Scripter
Yup!  This is the function I use for that: let isJumpgate = ()=>{ if(['jumpgate'].includes(Campaign().get('_release'))) { isJumpgate = () => true; } else { isJumpgate = () => false; } return isJumpgate(); }; You could also just check: if( "jumpgate" === Campaign().get('_release')) { I use the function because it caches the result and just becomes a return of true or false on future invocations, and it allows for future expansion with other releases.
1734278998
Ulti
Pro
Sheet Author
API Scripter
Thanks!
Thanks for replying The Aaron. Sounds like we're in much better shape than I was expecting for Jumpgate. Thank you also for the caveat regarding the 2024 sheet and script interaction. I will wait until until scripts can interact with it before considering moving to 2024.
1734545322
The Aaron
Roll20 Production Team
API Scripter
The Aaron said: sendPing() is not working currently (though it is being actively worked on!), so pingme and autoping aren't going to be useful (though they won't crash or anything, just won't do the pinging).  Once the ping functionality is fixed, they will just start working as normal.  Scripts that have ping as part of them will similarly have that functionality missing until the function is fixed (TurnMarker1, possibly Teleport) Just a heads up, sendPing() is now fixed on Jumpgate!
1734563325
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Much rejoicing!