diff --git a/InitiativeAssistant/InitiativeAssistant.js b/InitiativeAssistant/InitiativeAssistant.js new file mode 100644 index 0000000000..4ac227822f --- /dev/null +++ b/InitiativeAssistant/InitiativeAssistant.js @@ -0,0 +1,347 @@ +// Github: https://github.com/shdwjk/Roll20API/blob/master/InitiativeAssistant/InitiativeAssistant.js +// By: The Aaron, Arcane Scriptomancer +// Contact: https://app.roll20.net/users/104025/the-aaron + +var InitiativeAssistant = InitiativeAssistant || (function() { + 'use strict'; + + var version = '0.1.2', + lastUpdate = 1429545338, + schemaVersion = 0.2, + sorters = { + 'None': function(to) { + return to; + }, + 'Ascending': function(to){ + return _.sortBy(to,function(i){ + return (i.pr); + }); + }, + 'Descending': function(to){ + return _.sortBy(to,function(i){ + return (-i.pr); + }); + } + }, + + checkInstall = function() { + log('-=> InitiativeAssistant v'+version+' <=- ['+(new Date(lastUpdate*1000))+']'); + + if( ! _.has(state,'InitiativeAssistant') || state.InitiativeAssistant.version !== schemaVersion) { + log(' > Updating Schema to v'+schemaVersion+' <'); + state.InitiativeAssistant = { + version: schemaVersion, + config: { + sortOption: 'None' + } + }; + } + }, + + + 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 ''; + }, + + getConfigOption_SortOptions = function() { + var text = state.InitiativeAssistant.config.sortOption; + return '
'+ + 'Sort Options is currently '+ + text+ + '.'+ + '
'+ + _.map(_.keys(sorters),function(so){ + return ''+ + so+ + ''; + }).join(' ')+ + '
'+ + '
'; + }, + + showHelp = function(who) { + sendChat('','/w '+who+' ' + +'
' + +'
' + +'InitiativeAssistant v'+version + +'
' + +'
' + +'

Provides an easy interface to adding players into the initiative, particularly if they are manually rolling.

' + +'
' + +'Commands' + +'
' + +'!init-assist [ [--'+ch('<')+'name fragment'+ch('>')+'|'+ch('<')+'number'+ch('>')+'] ...] | --help' + +'
' + +'

Adds one or more characters to the initiative order.

' + +'
    ' + +'
  • ' + +''+ch('<')+'name fragment'+ch('>')+' '+ch('-')+' A part of the name of the character to add. This can be the full name, or just a few letters. Case-insensitive.' + +'
  • ' + +'
  • ' + +''+ch('<')+'number'+ch('>')+' '+ch('-')+' A number or inline roll representing the character'+ch("'")+'s initiative score.' + +'
  • ' + +'
' + +'
' + +'
' + +getConfigOption_SortOptions() + +'
' + ); + }, + + + keyFormat = function(text) { + return text.toLowerCase().replace(/\s+/,''); + }, + + handleInput = function(msg_orig) { + var msg = _.clone(msg_orig), + output='', + redos={}, + keys=[], + lookup, + chars, + args, + who, + to; + + if (msg.type !== "api" || !playerIsGM(msg.playerid)) { + return; + } + who=getObj('player',msg.playerid).get('_displayname').split(' ')[0]; + + if(_.has(msg,'inlinerolls')){ + msg.content = _.chain(msg.inlinerolls) + .reduce(function(m,v,k){ + m['$[['+k+']]']=v.results.total || 0; + return m; + },{}) + .reduce(function(m,v,k){ + return m.replace(k,v); + },msg.content) + .value(); + } + + args = msg.content + .replace(/\n/g, ' ') + .replace(/(\{\{(.*?)\}\})/g," $2 ") + .split(/\s+--/); + + switch(args.shift()) { + case '!init-assist': + if( !args.length || _.contains(args,'--help')) { + showHelp(who); + return; + } + + lookup = _.reduce(args,function(m,p){ + var parts=p.split(/\|/), + key=keyFormat(parts[0]); + + keys.push(key); + + m[key]={ + key: key, + input: parts[0], + init: parts[1] + }; + return m; + },{}); + + chars = _.reduce( + filterObjs(function(obj){ + return ('character' === obj.get('type')) && (_.reduce(keys,function(m,k){ + return m || (-1 !== keyFormat(obj.get('name')||'').indexOf(k)); + },false)); + }), + function(m,c){ + var ckey=keyFormat(c.get('name')||''), + key=_.find(keys,function(k){ + return (-1 !== ckey.indexOf(k)); + }); + m[key] = (m[key] ? m[key].push(c) && m[key] : [c]); + return m; + }, + {} + ); + + to=JSON.parse(Campaign().get('turnorder')) || []; + _.each(keys, function(k){ + var char; + if(chars[k]) { + if(1 === chars[k].length) { + char = findObjs({ + type: 'graphic', + pageid: Campaign().get('playerpageid'), + subtype: 'token', + represents: chars[k][0].id + })[0]; + if(char) { + to = _.reject(to, function(i){ + return char.id === i.id; + }); + to.push({ + id: char.id, + pr: lookup[k].init + }); + } else { + lookup[k].matches=chars[k]; + redos.NT=(redos.NT ? redos.NT.push(lookup[k]) && redos.NT : [lookup[k]]); + } + } else { + lookup[k].matches=chars[k]; + redos.DUP=(redos.DUP ? redos.DUP.push(lookup[k]) && redos.DUP : [lookup[k]]); + } + } else { + redos.NM=(redos.NM ? redos.NM.push(lookup[k]) && redos.NM : [lookup[k]]); + } + }); + Campaign().set({ + turnorder: JSON.stringify(sorters[state.InitiativeAssistant.config.sortOption](to)) + }); + + _.each(redos,function(rs,k){ + var params=[]; + switch(k){ + case 'NT': + output+= + '
'+ + '

Please Add Tokens

'+ + '
'+ + _.map(rs, function(r){ + var c = r.matches[0]; + params.push('--'+r.key+'|'+r.init); + return '
'+ + ''+ + ''+c.get("name")+''+ + '
'+ + '
'; + }).join(' ')+ + '
'+ + '

After adding tokens for the above characters: Add Turn(s)

'+ + '
'; + break; + case 'DUP': + output+= + '
'+ + '

Which One?

'+ + '
'+ + _.map(rs, function(r){ + return '

'+ + r.input+ + '

'+ + '
'+ + _.map(r.matches,function(c){ + var button='Pick'; + return '
'+ + ''+ + button+ + ''+c.get("name")+''+ + '
'+ + '
'; + }).join('')+ + '
'; + }).join(' ')+ + '

'+ + '
'; + break; + case 'NM': + output+= + '
'+ + '

No Matching Characters

'+ + '
'+ + _.map(rs, function(r){ + return '
'+ + r.input+ + '
'; + }).join(' ')+ + '
'+ + '
'; + break; + } + }); + if(output){ + sendChat('Initiative Assistant','/w '+who+' '+output); + } + + break; + case '!init-assist-config': + if(_.contains(args,'--help')) { + showHelp(who); + return; + } + if(!args.length) { + sendChat('','/w '+who+' ' + +'
' + +'
' + +'InitiativeAssistant v'+version + +'
' + +getConfigOption_SortOptions() + +'
' + ); + return; + } + _.each(args,function(a){ + var opt=a.split(/\|/), + msg=''; + switch(opt.shift()) { + case 'sort-option': + if(sorters[opt[0]]) { + state.InitiativeAssistant.config.sortOption=opt[0]; + } else { + msg='
Error: Not a valid sort method: '+opt[0]+'
'; + } + sendChat('','/w '+who+' ' + +'
' + +msg + +getConfigOption_SortOptions() + +'
' + ); + break; + + default: + sendChat('','/w '+who+' ' + +'
Unsupported Option:
'+a+'' + ); + } + + }); + + break; + } + }, + + registerEventHandlers = function() { + on('chat:message', handleInput); + }; + + return { + CheckInstall: checkInstall, + RegisterEventHandlers: registerEventHandlers + }; + +}()); + +on('ready',function() { + 'use strict'; + + InitiativeAssistant.CheckInstall(); + InitiativeAssistant.RegisterEventHandlers(); +}); diff --git a/InitiativeAssistant/package.json b/InitiativeAssistant/package.json new file mode 100644 index 0000000000..2e190df1b8 --- /dev/null +++ b/InitiativeAssistant/package.json @@ -0,0 +1,17 @@ +{ + "name": "InitiativeAssistant", + "version": "0.1.2", + "description": "Provides an easy interface to adding players to the inititaive order.", + "authors": "The Aaron", + "roll20userid": "104025", + "dependencies": { + }, + "modifies": { + "state.InitiativeAssistant": "read,write", + "campaign.turnorder": "read,write", + "character.name": "read", + "token.represents": "read" + }, + "conflicts": [ + ] +}