/* 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. Possible new Features: Find a way to recognize destination tokens without needing StatusMarker (possibly through use of State and an update command) Update State on some command (!nav config maybe) On update command activation do a search for GMlayer and Map tokens Campaign wide with names equal to a page's name (using .indexOf() probably) and load those into state.PAGENAVIGATION.destinations (protype name) Does not update this unless told to do so. !nav config could become an options menu with API buttons for determining overall script workings Add the ability to manually move individual players back to the player ribbon (reverse of splitting the party) probably through an API button generation similar to the one for !nav pages that would create a button for each player and delete their playerspecificpages entry if pressed Potentially set limits on ability of players to move other players around pages. would use the state, and probably be part of !nav config Reduce the clutter of the help screen: perhaps general descriptions in initial help screen with button activated details? Find a way to eliminate the second word in two part names entering into the chat. Add handling for Names (Player and Character and token if possible), Character IDs. */ var PAGENAVIGATOR= PAGENAVIGATOR|| (function(){ 'use strict'; var version = '0.1.031', lastUpdate = 1463700815, schemaVersion = 1.031, 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 = {}, /*Provides handling of troublesome characters for sendChat*/ 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 ''; }, /*Builds templates for use in all other functions*/ 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( ' href="<%= command %>"><%= label||"Button" %>' ); }, /*Makes the API buttons used throughout the script*/ makeButton = function(command, label, backgroundColor, color){ return templates.button({ command: command, label: label, templates: templates, defaults: defaults, css: { color: color, 'background-color': backgroundColor } }); }, /*Checks the API environment to make sure everything is prepped for the script*/ 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(); }, /*mapConfig = function() { Proposed Configs: Accesses: Whole Party: player/GM } Current Party: player/GM }-These settings affect who can be moved by who Specific Player: player/GM } All/CurrentParty/Self Only }-Affects whether a player can specifically move all players, the current party only, or only themselves with !nav pages ... Identifying Player accessible pages: Players Access all pages: On/Off Identification Mode: Statusmarker/By Name Statusmarker: Cycle Through List of statusmarkers if ID mode is set to 'Statusmarker' Update: Updates the destination pages if ID mode is set to 'By Name' } */ /*Moves a specific player or group to a page*/ 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}); }, /*Moves the current party (maintains split party) to a specific page*/ moveCurrentParty = function(destination){ sendChat('Page Navigator', 'The current party is being moved to a new map.'); Campaign().set({playerpageid: destination}); }, /*Moves all players to a specific page and moves everyone back to the party ribbon*/ moveAllPlayers = function(destination){ sendChat('Page Navigator', 'All players are being moved to a new map'); Campaign().set({playerpageid: destination, playerspecificpages: false}); }, /*Determines if there was a collision between a character-token and a destination-token and calls for API permission buttons be made and outputs them to chat*/ 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') +'
' + msg + '
' + '
' + msg + '
' + '
' + msg + '
' + '
' + msg + '
' + '
Page Navigator allows you to more easily move your players from page to page. You can utilize the script through API commands or by relying on token collisions to provide a more interactive map for your players.
' +'Click here for details on how to define destination tokens.
Click here for details on chat commands for the script using !nav
Click here for details on all the various API butons which are generated by the script.
Click here for details on setting different access levels for specific pages and what exactly player vs. gm access means