// Github: https://github.com/shdwjk/Roll20API/blob/master/Mark/Mark.js // By: The Aaron, Arcane Scriptomancer // Contact: https://app.roll20.net/users/104025/the-aaron var Mark = Mark || (function() { 'use strict'; var version = '0.3.5', lastUpdate = 1514912751, schemaVersion = 0.2, markerURL = 'https://s3.amazonaws.com/files.d20.io/images/4994795/7MdfzjgXCkaESbRbxATFSw/thumb.png?1406949835', 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(who) { sendChat('', '/w "'+who+'" ' +'
' +'
' +'Mark v'+version +'
' +'
' +'

Mark places a numbered marker under each token whose id is supplied ' +'to it. Markers are cleared when the Turn Order changes, is closed, ' +'or when the player page changes. This script is intended to allow players ' +'to mark their targets for discussion with the GM, usually as part of an ' +'attack.

' +'
' +'Commands' +'
' +'!mark '+ch('<')+'Token ID'+ch('>')+' ['+ch('<')+'Token ID'+ch('>')+' ... ]' +'
' +'

This command requires a minimum of 1 parameter. For each supplied Token ID, a marker is placed beneath it with a numbered status. The status number starts at 1, increases with each marker placed, and resets the when markers are cleared.

' +'

Note: If you are using multiple '+ch('@')+ch('{')+'target'+ch('|')+'token_id'+ch('}')+' calls in a macro, and need to mark fewer than the suppled number of arguments, simply select the same token several times. The duplicates will be removed.

' +'
    ' +'
  • ' +''+ch('<')+'Token ID'+ch('>')+' '+ch('-')+' A Token ID, usually supplied with something like '+ch('@')+ch('{')+'target'+ch('|')+'Target 1'+ch('|')+'token_id'+ch('}')+'.' +'
  • ' +'
' +'
' +'!mark-clear' +'
' +'

Clears all the markers. (GM Only)

' +'
' +'
' +'
' ); }, reset = function() { state.Mark.count=0; _.each(findObjs({ type: 'graphic', subtype: 'token', imgsrc: markerURL }), (g)=>g.remove()); }, getStatusForCount = function(count) { var colorOrder=["red", "blue", "green", "brown", "purple", "pink", "yellow"]; return _.chain(count.toString().split('')) .reduce(function(memo,d){ if(colorOrder.length) { memo.push(colorOrder.shift()+'@'+d); } return memo; }, []) .value() .reverse() .join(','); }, handleInput = function(msg) { var args, who, errors=[], tokens; if (msg.type !== "api" ) { return; } who=(getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); args = msg.content.split(/ +/); switch(args[0]) { case '!mark-clear': if(playerIsGM(msg.playerid)) { reset(); } break; case '!mark': if(1 === args.length) { showHelp(who); break; } tokens=_.chain(args) .rest() .uniq() .map(function(a){ var t=getObj('graphic',a); if(! t) { errors.push('Argument ['+a+'] is not a valid token id.'); } return t; },errors) .filter(function(t){ return undefined !== t; }) .value(); if(errors.length) { sendChat('','/w "'+who+'" ' +'
' +'
Error: ' +errors.join('
Error: ') +'
' +'
' ); } _.each(tokens, function (t) { let size=( Math.max(t.get('width'), t.get('height') ) * 1.7), count=++state.Mark.count, status=getStatusForCount(count), m = createObj('graphic',{ imgsrc: markerURL, subtype: 'token', pageid: t.get('pageid'), width: size, height: size, top: t.get('top'), left: t.get('left'), layer: 'objects', statusmarkers: status }); toBack(m); }); break; } }, checkInstall = function() { log('-=> Mark v'+version+' <=- ['+(new Date(lastUpdate*1000))+']'); if( ! _.has(state,'Mark') || state.Mark.version !== schemaVersion) log(' > Updating Schema to v'+schemaVersion+' <'); { /* Default Settings stored in the state. */ state.Mark = { version: schemaVersion, count: 0, markedtokens: {} }; } }, handlePlayerPageChange = function() { reset(); }, handleTurnOrderChange = function(obj,prev) { var to = JSON.parse(obj.get("turnorder")), po = JSON.parse(prev.turnorder); if( (_.isArray(to) && _.isArray(po) && !_.isEqual(to[0],po[0]) ) || (_.isArray(to) && ! _.isArray(po)) ) { reset(); } }, handleInitiativePageChange = function(obj) { if(false === obj.get('initiativepage')) { reset(); } }, registerEventHandlers = function() { on('chat:message', handleInput); on('change:campaign:playerpageid', handlePlayerPageChange); on('change:campaign:turnorder', handleTurnOrderChange); on('change:campaign:initiativepage', handleInitiativePageChange); }; return { RegisterEventHandlers: registerEventHandlers, CheckInstall: checkInstall, Reset: reset }; }()); on("ready",function(){ 'use strict'; Mark.CheckInstall(); Mark.RegisterEventHandlers(); });