Instantly share code, notes, and snippets.
Last active
January 25, 2017 02:11
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(1)
1
You must be signed in to fork a gist
-
Save Kurohyou/5c6a1d64bbd55f132c66b0a712522a60 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
PAGENAVIGATOR script: | |
Author: Scott C. | |
Contact: https://app.roll20.net/users/459831/scott-c | |
Thanks to: The Aaron, Arcane Scriptomancer and Stephen for their help with the bulk of this script. | |
Script goal: to simplify moving between maps in Roll20. | |
*/ | |
var PAGENAVIGATOR= PAGENAVIGATOR|| (function(){ | |
'use strict'; | |
var version = '0.1.03', | |
lastUpdate = 1463518426, | |
schemaVersion = 1.03, | |
defaults = { | |
css: { | |
button: { | |
'border': '1px solid #cccccc', | |
'border-radius': '1em', | |
'background-color': '#006dcc', | |
'margin': '0 .1em', | |
'font-weight': 'bold', | |
'padding': '.1em 1em', | |
'color': 'white' | |
} | |
} | |
}, | |
templates = {}, | |
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 ''; | |
}, | |
buildTemplates = function() { | |
templates.cssProperty =_.template( | |
'<%=name %>: <%=value %>;' | |
); | |
templates.style = _.template( | |
'style="<%='+ | |
'_.map(css,function(v,k) {'+ | |
'return templates.cssProperty({'+ | |
'defaults: defaults,'+ | |
'templates: templates,'+ | |
'name:k,'+ | |
'value:v'+ | |
'});'+ | |
'}).join("")'+ | |
' %>"' | |
); | |
templates.button = _.template( | |
'<a <%= templates.style({'+ | |
'defaults: defaults,'+ | |
'templates: templates,'+ | |
'css: _.defaults(css,defaults.css.button)'+ | |
'}) %> href="<%= command %>"><%= label||"Button" %></a>' | |
); | |
}, | |
makeButton = function(command, label, backgroundColor, color){ | |
return templates.button({ | |
command: command, | |
label: label, | |
templates: templates, | |
defaults: defaults, | |
css: { | |
color: color, | |
'background-color': backgroundColor | |
} | |
}); | |
}, | |
checkInstall = function() { | |
log('-=> Page Navigator v'+version+' <=- ['+(new Date(lastUpdate*1000))+']'); | |
if( ! _.has(state,'PAGENAVIGATOR') || state.PAGENAVIGATOR.version !== schemaVersion) { | |
log(' > Updating Schema to v'+schemaVersion+' <'); | |
switch(state.PAGENAVIGATOR&& state.PAGENAVIGATOR.version) { | |
case 'UpdateSchemaVersion': | |
state.PAGENAVIGATOR.version = schemaVersion; | |
break; | |
default: | |
state.PAGENAVIGATOR= { | |
version: schemaVersion, | |
}; | |
break; | |
} | |
} | |
buildTemplates(); | |
}, | |
movePlayer = function(destination, playerid){ | |
let pp = Campaign().get('playerspecificpages'); | |
pp = (_.isObject(pp) ? pp : {} ); | |
var iteration = 0; | |
sendChat('Page Navigator', 'Some players are being split from the party.'); | |
_.each(playerid, function(){ | |
pp[playerid[iteration]] = destination; | |
iteration++; | |
}); | |
Campaign().set({playerspecificpages: false}); | |
Campaign().set({playerspecificpages: pp}); | |
}, | |
moveCurrentParty = function(destination){ | |
sendChat('Page Navigator', 'The current party is being moved to a new map.'); | |
Campaign().set({playerpageid: destination}); | |
}, | |
moveAllPlayers = function(destination){ | |
sendChat('Page Navigator', 'All players are being moved to a new map'); | |
Campaign().set({playerpageid: destination, playerspecificpages: false}); | |
}, | |
getDestinationCollisions = function(token) { | |
var pageId, | |
destinations, | |
collisions, | |
tokenControl, | |
controlList = '', | |
iteration = 0, | |
character, | |
msg = '', | |
destination, | |
overlapCollision, | |
lastCollision; | |
pageId = token.get('_pageid'); | |
destinations = getDestinations(pageId); | |
collisions = TokenCollisions.getCollisions(token, destinations); | |
lastCollision = _.last(collisions); | |
if(token.get('represents')){ | |
character = getObj('character', token.get('represents')); | |
tokenControl = character.get('controlledby').split(","); | |
}else{ | |
tokenControl = token.get('controlledby').split(","); | |
} | |
if(lastCollision && token.get('name') && tokenControl){ | |
overlapCollision = TokenCollisions.isOverlapping(token, lastCollision, false); | |
if(overlapCollision){ | |
destination = findObjs({ | |
type: 'page', | |
name: lastCollision.get('name') | |
}); | |
if(lastCollision && _.contains(tokenControl, 'all') && destination[0]){ | |
msg += makeButton( | |
'!nav whole ' + destination[0].id, | |
'Whole Party', '#fff8dc', '#191970' | |
); | |
msg += makeButton( | |
'!nav current ' + destination[0].id, | |
'Current Party', '#fff8dc', '#191970' | |
); | |
} else if(lastCollision && !_.contains(tokenControl, 'all') && destination[0]){ | |
_.each(tokenControl, function(){ | |
controlList += tokenControl[iteration] + ' '; | |
iteration++; | |
}) | |
iteration = 0; | |
msg += makeButton( | |
'!nav whole ' + destination[0].id, | |
'Whole Party', '#fff8dc', '#191970' | |
); | |
msg += makeButton( | |
'!nav current ' + destination[0].id, | |
'Current Party', '#fff8dc', '#191970' | |
); | |
msg += makeButton( | |
'!nav player ' + destination[0].id + ' ' + controlList, | |
'Controlling Player(s)', '#fff8dc', '#191970' | |
); | |
} | |
if(destination[0]){ | |
if(tokenControl && destination[0].get('name').indexOf('"players"')>=0){ | |
sendChat('Page Navigation', '/w ' + token.get('name') +'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">' | |
+'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 110%;">' | |
+ token.get('name') + ' would like to move to ' + destination[0].get('name') + '. Who is traveling with them? </div>' | |
+'<p>' + msg + '<p>' + | |
'</div>' | |
); | |
} | |
sendChat('Page Navigation', '/w gm <div style="border: 1px solid black; background-color: white; padding: 3px 3px;">' | |
+'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 110%;">' | |
+ token.get('name') + ' would like to move to ' + destination[0].get('name') + '. Who is traveling with them? </div>' | |
+'<p>' + msg + '<p>' + | |
'</div>' | |
); | |
} | |
} | |
} | |
}, | |
getDestinations = function(pageId){ | |
return findObjs({ | |
pageid: pageId, | |
type: 'graphic', | |
layer: /*'map' ||*/ 'gmlayer', | |
statusmarkers: 'flying-flag' | |
}); | |
}, | |
/*finds all the nonarchived pages in the campaign | |
*/ | |
getPages = function(access) { | |
var pages = findObjs({ | |
type: 'page', | |
archived: false | |
}), | |
fpages; | |
if(access === 'player'){ | |
fpages = _.filter(pages, function(p){ | |
return (p.get('name').indexOf('"players"')>=0); | |
}) | |
return fpages; | |
}else{ | |
return pages; | |
} | |
}, | |
/*Takes an array of tokens and gets who they are controlled by. Creates API buttons to send the indicated player(s) to specific maps and outputs thos buttons | |
to Chat. | |
*/ | |
pagesPlayerDialog = function(whoToMove, access, speaker) { | |
var pages = getPages(access), | |
msg = '', | |
tokensControl, | |
allCount = 0, | |
iteration = 0, | |
tokensControl = _.map(whoToMove, function(obj){ | |
return obj[0].get('controlledby').split(','); | |
}), | |
players, | |
character = _.map(whoToMove, function(obj){ | |
log('obj represents:'); | |
log(obj[0].get('represents')); | |
return findObjs({ | |
type: 'character', | |
id: obj[0].get('represents') | |
}); | |
}), | |
idList = '', | |
names, | |
nameList = ''; | |
if(character){ | |
tokensControl = _.map(character, function(obj){ | |
return obj[0].get('controlledby').split(','); | |
}); | |
log('each tokensControl:'); | |
log(tokensControl); | |
}else{ | |
tokensControl = _.map(whoToMove, function(obj){ | |
return obj[0].get('controlledby').split(','); | |
}); | |
} | |
if(pages.length === 0 || !pages){ | |
sendChat('Page Navigation', '/w ' + speaker + ' <div style="border: 1px solid black; background-color: white; padding: 3px 3px;">' | |
+'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 100%;">' | |
+' There are no valid destinations. This is either because there are no pages in the campaign, or because the GM has not specified any as player accessible via adding <b>"players"</b> to the page name.' | |
); | |
} | |
_.each(tokensControl, function(){ | |
if(_.contains(tokensControl[iteration], 'all') && allCount === 0){ | |
pagesPartyDialog('all'); | |
allCount++; | |
} | |
iteration++; | |
}); | |
iteration = 0; | |
if(allCount===0){ | |
players = _.map(tokensControl, function(id){ | |
return findObjs({ | |
type: 'player', | |
id: id[0] | |
}); | |
}); | |
names = _.map(players, function(obj){ | |
return obj[0].get('_displayname'); | |
}); | |
_.each(names, function(){ | |
nameList += names[iteration] + ' '; | |
iteration++; | |
}) | |
iteration = 0; | |
_.each(tokensControl, function(){ | |
idList += tokensControl[iteration] + ' '; | |
iteration++; | |
}) | |
iteration = 0; | |
_.each(pages, function(){ | |
msg += makeButton( | |
'!nav player ' + pages[iteration].id + ' ' + idList, | |
pages[iteration].get('name'), '#fff8dc', '#191970' | |
); | |
iteration++; | |
}); | |
sendChat('Page Navigation', '/w ' + speaker + ' <div style="border: 1px solid black; background-color: white; padding: 3px 3px;">' | |
+'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 110%;">' | |
+ 'As ' + speaker + ' you can move ' + nameList + ' to:' + '</div>' | |
+'<p>' + msg + '<p>' + | |
'</div>' | |
); | |
}; | |
}, | |
/* Makes and outputs buttons to send the whole party, or just the current party to the indicated page*/ | |
pagesPartyDialog = function(whoToMove, access, speaker) { | |
var pages = getPages(access), | |
msg = '', | |
iteration = 0; | |
log(pages.length); | |
log(pages); | |
if(pages.length >= 1 && whoToMove === 'whole' || whoToMove === 'current') { | |
log('make button if entered'); | |
_.each(pages, function(){ | |
msg += makeButton( | |
'!nav ' + whoToMove + ' ' + pages[iteration].id, | |
pages[iteration].get('name'), '#fff8dc', '#191970' | |
); | |
iteration++; | |
}); | |
sendChat('Page Navigation', '/w ' + speaker + ' <div style="border: 1px solid black; background-color: white; padding: 3px 3px;">' | |
+'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 110%;">' | |
+ 'As ' + speaker + ' you can set the ' + whoToMove + ' party' + ch("'") + 's page to:</div>' | |
+'<p>' + msg + '<p>' + | |
'</div>' | |
); | |
return; | |
}else if(pages.length === 0){ | |
sendChat('Page Navigation', '/w ' + speaker + ' <div style="border: 1px solid black; background-color: white; padding: 3px 3px;">' | |
+'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 100%;">' | |
+' There are no valid destinations. This is either because there are no pages in the campaign, or because the GM has not specified any as player accessible via adding <b>"players"</b> to the page name.' | |
); | |
} | |
}, | |
showHelp = function(who) { | |
sendChat('','/w "'+who+'" ' | |
+'<div style="border: 1px solid black; background-color: white; padding: 3px 3px;">' | |
+'<div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;">' | |
+'Page Navigator v'+version | |
+'</div>' | |
+'<div style="padding-left:10px;margin-bottom:3px;">' | |
+'<p>Page Navigator allows you to more easily move your players from page to page.</p>' | |
+'</div>' | |
+'<b>Commands</b>' | |
+'<div style="padding-left:10px;">' | |
+'<b><span style="font-family: serif;">!nav</span></b>' | |
+'<div style="padding-left: 10px;padding-right:20px">' | |
+'<p>The api command to trigger the script.</p>' | |
+'<ul>' | |
+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">' | |
+'<b><span style="font-family: serif;">help</span></b> '+ch('-')+' Shows the Help screen' | |
+'</li> ' | |
+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">' | |
+'<b><span style="font-family: serif;">pages</span></b> '+ch('-')+' Brings up a dialog of all non-archived pages in the Campaign for specifying a page to send players to.' | |
+'<br>The exact behavior is dependent on the next argument, which can be <b>"whole"</b>, <b>"current"</b>, or <b>"player"</b>. <br>If "player" is the next argument, it needs to be followed by a list of token_ids passed via '+ch('@')+'{target|target1|token_id} ... '+ch('@')+'{target|targetn|token_id} or '+ch('@')+'{selected|token_id (only the id(s) of one token can be passed this way)}.' | |
+'</li> ' | |
+'</div>' | |
+'<b>Buttons</b>' | |
+'<ul><li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">' | |
+'<b><span style="font-family: serif;">Whole Party</span></b> '+ch('-')+' Moves the player ribbon to the destination page and moves all players to the player ribbon.' | |
+'</li> ' | |
+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">' | |
+'<b><span style="font-family: serif;">Current Party</span></b> '+ch('-')+' Moves the player ribbon to the destination page, but does not affect any players split from the party.' | |
+'</li> ' | |
+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">' | |
+'<b><span style="font-family: serif;">Controlling Player(s)</span></b> '+ch('-')+' Moves the player(s) who controls the triggering token to the destination page (splits the party).' | |
+'</li> ' | |
+'</ul>' | |
+'<b>Access</b>' | |
+'<ul><li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">' | |
+'<b><span style="font-family: serif;">Setting Access</span></b> '+ch('-')+' Pages are GM access only by default. To set a page to Player access, simply add <b>"players"</b> anywhere in the page name.' | |
+'<ul>' | |
+'Example: <b>Test Page</b> - GM only page<br>vs.<br><b>Test Page "players"</b> - player visible page' | |
+'</ul>' | |
+'</li> ' | |
+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">' | |
+'<b><span style="font-family: serif;">GM Access</span></b> '+ch('-')+' If the !nav pages ... command is sent from a GM, all pages are populated as API buttons. The GM receives a whispered movement prompt for all token collision prompted navigation requests.' | |
+'</li> ' | |
+'<li style="border-top: 1px solid #ccc;border-bottom: 1px solid #ccc;">' | |
+'<b><span style="font-family: serif;">Player Access</span></b> '+ch('-')+' If the !nav pages ... command is sent from a player, only those pages flagged as player pages are populated as API buttons. Players only receive a whispered movement prompt when their token collides with a player accessible destination.' | |
+'</li> ' | |
+'</ul>' | |
+'</div>' | |
+'</div>' | |
); | |
}, | |
handleInput = function(msg_orig){ | |
var msg = _.clone(msg_orig), | |
args, | |
who, | |
page, | |
tokens, | |
tokenIds, | |
iteration = 0, | |
player, | |
speaker, | |
access; | |
if (msg.type !== "api") { | |
return; | |
} | |
if(playerIsGM(msg.playerid)) { | |
access = 'gm'; | |
speaker = 'gm'; | |
}else if(!playerIsGM(msg.playerid)){ | |
access = 'player'; | |
speaker = msg.who; | |
} | |
who=getObj('player',msg_orig.playerid).get('_displayname'); | |
args = msg.content.split(/\s/); | |
switch(args[0]) { | |
case '!nav': | |
if(_.contains(args,'help')) { | |
showHelp(who); | |
return; | |
} | |
switch(args[1]){ | |
case 'whole': | |
if(args[2]){ | |
moveAllPlayers(args[2]); | |
}else{ | |
sendChat('','/w "'+who+'" '+ | |
'<div><b>No pageid associated with that destination:</div> </div>' | |
); | |
} | |
break; | |
case 'current': | |
if(args[2]){ | |
moveCurrentParty(args[2]); | |
} else{ | |
sendChat('','/w "'+who+'" '+ | |
'<div><b>No pageid associated with that destination:</div> </div>' | |
); | |
} | |
break; | |
case 'player': | |
if(args[2] && _.rest(args, 3)){ | |
movePlayer(args[2], _.rest(args, 3)); | |
}else{ | |
sendChat('','/w "'+who+'" '+ | |
'<div><b>Player case Error: No pageid associated with that destination or no specific player named:</div> </div>' | |
); | |
} | |
break; | |
case 'pages': | |
if(args[2]){ | |
if(args[2] === 'whole' || args[2] === 'current'){ | |
pagesPartyDialog(args[2], access, speaker); | |
}else if(args[2] === 'player' && _.rest(args, 3)){ | |
tokens = _.map(_.rest(args,3), function(id){ | |
return findObjs({ | |
type: 'graphic', | |
id: id | |
}); | |
}); | |
pagesPlayerDialog(tokens, access, speaker); | |
//sendChat('Page Navigator', '/w ' + who + ' The player case is not finished yet and does not do anything atm.'); | |
//pagesPlayerDialog(tokens); | |
} | |
}else{ | |
sendChat('Page Navigator', '/w ' + who + ' The previous !nav command did not have sufficient arguments.'); | |
showHelp(who); | |
} | |
} | |
break; | |
} | |
}, | |
RegisterEventHandlers = function() { | |
on('change:graphic', getDestinationCollisions); | |
on('chat:message', handleInput); | |
}; | |
return { | |
CheckInstall: checkInstall, | |
RegisterEventHandlers: RegisterEventHandlers | |
}; | |
}()); | |
on("ready",function(){ | |
'use strict'; | |
PAGENAVIGATOR.CheckInstall(); | |
PAGENAVIGATOR.RegisterEventHandlers(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment