Created
March 21, 2018 13:17
-
-
Save dancodan/0fa47173ad74418bae749d2c7899cb9c to your computer and use it in GitHub Desktop.
This file contains hidden or 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
var RealRollableTable = RealRollableTable || (function() { | |
'use strict'; | |
var version = 1, | |
tables = _.groupBy(findObjs({ type: 'rollabletable' }), function(table) { return table.get('name').toLowerCase(); }), | |
commands = { | |
rt: function(args, msg) { | |
var i, items, roll, table; | |
if (args.length < 2) { | |
commands.help_rt(args, msg); | |
return; | |
} | |
table = tables[args[0].toLowerCase()]; | |
if (!table) { | |
table = findObjs({ | |
type: 'rollabletable', | |
name: args[0] | |
}, { caseInsensitive: true })[0]; | |
if (table) { | |
tables[args[0].toLowerCase()] = table; | |
} else { | |
sendChat(getSystemFrom(), getWhisperTarget({ player: true, id: msg.playerid }) + | |
'Could not find table "' + args[0] + '". Please supply an existing table name.'); | |
return; | |
} | |
} | |
items = []; | |
_.each(findObjs({ | |
type: 'tableitem', | |
rollabletableid: table.id | |
}), function(element, index, list) { | |
var i, | |
weight = parseInt(element.get('weight')); | |
for (i = 0; i < weight; i++) { | |
items.push(element); | |
} | |
}); | |
roll = _.rest(args).join(' '); | |
if (msg.inlinerolls) { | |
for (i = 0; i < msg.inlinerolls.length; i++) { | |
roll = roll.replace('$[[' + i + ']]', msg.inlinerolls[i].results.total); | |
} | |
} | |
sendChat('', '/r ' + roll, function(ops) { | |
var rollresult = JSON.parse(ops[0].content), | |
actualRoll = roll, | |
displayTotal = rollresult.total, | |
dieSize, tableItem, tableItemAvatar, tableItemIndex, tableItemName; | |
if (rollresult.resultType === 'M') { | |
// Math-only roll | |
dieSize = Math.min(parseInt(items.length - rollresult.total), 0); | |
actualRoll = '1d' + dieSize + ' + ' + roll; | |
displayTotal = (dieSize > 0 ? randomInteger(dieSize) : 0) + rollresult.total; | |
tableItemIndex = Math.max(Math.min(displayTotal - 1, items.length - 1), 0); | |
} else { | |
tableItemIndex = Math.max(Math.min(rollresult.total - 1, items.length - 1), 0); | |
} | |
tableItem = items[tableItemIndex]; | |
tableItemAvatar = /*tableItem.get('avatar') ? '\n<img src="' + tableItem.get('avatar') + '" />' :*/ ''; | |
//tableItemAvatar = tableItemAvatar.replace(/med|max/, 'thumb'); | |
tableItemName = tableItem.get('name') ? '\n<span style="padding:3px;background-color:yellow"><b>' + | |
tableItem.get('name') + '</b></span>' : ''; | |
sendChat(getPlayerCharacterFrom(msg.who), 'Rolling ' + actualRoll + ' (' + displayTotal + | |
') on table "' + args[0] + '":' + tableItemAvatar + tableItemName); | |
}); | |
}, | |
help: function(command, args, msg) { | |
if (_.isFunction(commands['help_' + command])) { | |
commands['help_' + command](args, msg); | |
} | |
}, | |
help_rt: function(args, msg) { | |
sendChat(getSystemFrom(), getWhisperTarget({ player: true, id: msg.playerid }) + | |
'<div style="border:1px solid black;background:white;padding:3px 3px;">' + | |
'<div style="font-weight:bold;border-bottom:1px solid black;font-size:130%;">' + | |
'Real Rollable Tables v' + state.RealRollableTable.version + | |
'</div>' + | |
'<span style="font-family:consolas"><b>!rt</b> <i>table-name roll</i></span>' + | |
'<div style="padding-left:10px;margin-bottom:3px;">' + | |
'<p>Supply a rolltable table\'s <span style="font-family:consolas">table-name</span> and a ' + | |
'<span style="font-family:consolas">roll</span> to run on that table. If ' + | |
'<span style="font-family:consolas">roll</span> is a math expression rather than a roll ' + | |
'expression, a single die of appropriate size will be selected such that rolling the maximum ' + | |
'value on the die will result in the last value in the table. If table items have weights ' + | |
'greater than 1, they will be treated as consecutive entries in the table (eg, a table with ' + | |
'items a:1, b:3, c:1, d:1 would be equivalent to a table with items a, b, b, b, c, d). ' + | |
'<b>Fractional weights are ignored!</b></p>' + | |
'<p>If the result of <span style="font-family:consolas">roll</span> would be lower than 1 or ' + | |
'greater than the number of elements in the table, the first or last element of the table will be ' + | |
'used as the result, as appropriate.</p>' + | |
'</div>'); | |
} | |
}; | |
function getPlayerCharacterFrom(name) { | |
var character = findObjs({ | |
type: 'character', | |
name: name | |
})[0], | |
player = findObjs({ | |
type: 'player', | |
displayname: name.lastIndexOf(' (GM)') === name.length - 5 ? name.substring(0, name.length - 5) : name | |
})[0]; | |
if (player) { | |
return 'player|' + player.id; | |
} | |
if (character) { | |
return 'character|' + character.id; | |
} | |
return name; | |
} | |
function getWhisperTarget(options) { | |
var nameProperty, targets, type; | |
options = options || {}; | |
if (options.player) { | |
nameProperty = 'displayname'; | |
type = 'player'; | |
} else if (options.character) { | |
nameProperty = 'name'; | |
type = 'character'; | |
} else { | |
return ''; | |
} | |
if (options.id) { | |
targets = [getObj(type, options.id)]; | |
if (targets[0]) { | |
return '/w ' + targets[0].get(nameProperty).split(' ')[0] + ' '; | |
} | |
} | |
if (options.name) { | |
targets = _.sortBy(filterObjs(function(obj) { | |
if (obj.get('type') !== type) return false; | |
return obj.get(nameProperty).indexOf(options.name) >= 0; | |
}), function(obj) { | |
return Math.abs(levenshteinDistance(obj.get(nameProperty), options.name)); | |
}); | |
if (targets[0]) { | |
return '/w ' + targets[0].get(nameProperty).split(' ')[0] + ' '; | |
} | |
} | |
return ''; | |
} | |
function splitArgs(input, separator) { | |
var singleQuoteOpen = false, | |
doubleQuoteOpen = false, | |
tokenBuffer = [], | |
ret = [], | |
arr = input.split(''), | |
element, i, matches; | |
separator = separator || /\s/g; | |
for (i = 0; i < arr.length; i++) { | |
element = arr[i]; | |
matches = element.match(separator); | |
if (element === '\'') { | |
if (!doubleQuoteOpen) { | |
singleQuoteOpen = !singleQuoteOpen; | |
continue; | |
} | |
} else if (element === '"') { | |
if (!singleQuoteOpen) { | |
doubleQuoteOpen = !doubleQuoteOpen; | |
continue; | |
} | |
} | |
if (!singleQuoteOpen && !doubleQuoteOpen) { | |
if (matches) { | |
if (tokenBuffer && tokenBuffer.length > 0) { | |
ret.push(tokenBuffer.join('')); | |
tokenBuffer = []; | |
} | |
} else { | |
tokenBuffer.push(element); | |
} | |
} else if (singleQuoteOpen || doubleQuoteOpen) { | |
tokenBuffer.push(element); | |
} | |
} | |
if (tokenBuffer && tokenBuffer.length > 0) { | |
ret.push(tokenBuffer.join('')); | |
} | |
return ret; | |
} | |
function levenshteinDistance(a, b) { | |
var i, j, | |
matrix = []; | |
if (a.length === 0) { | |
return b.length; | |
} | |
if (b.length === 0) { | |
return a.length; | |
} | |
// Increment along the first column of each row | |
for (i = 0; i <= b.length; i++) { | |
matrix[i] = [i]; | |
} | |
// Increment each column in the first row | |
for (j = 0; j <= a.length; j++) { | |
matrix[0][j] = j; | |
} | |
// Fill in the rest of the matrix | |
for (i = 1; i <= b.length; i++) { | |
for (j = 1; j <= a.length; j++) { | |
if (b.charAt(i - 1) === a.charAt(j - 1)) { | |
matrix[i][j] = matrix[i - 1][j - 1]; | |
} else { | |
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // Substitution | |
Math.min(matrix[i][j - 1] + 1, // Insertion | |
matrix[i - 1][j] + 1)); // Deletion | |
} | |
} | |
} | |
return matrix[b.length][a.length]; | |
} | |
function getSystemId() { | |
// Get character id of `system` | |
var systemChar = findObjs({ | |
type: 'character', | |
name: 'system', | |
controlledby: '' | |
}, { caseInsensitive: true })[0]; | |
return systemChar ? systemChar.id : ''; | |
} | |
function getSystemFrom() { | |
// Get appropriate value of `who` parameter for `sendChat` to send messages by System | |
var systemChar, systemId; | |
if (state.RealRollableTable.systemId !== '') { | |
systemChar = getObj('character', state.RealRollableTable.systemId); | |
} | |
if (!systemChar) { | |
systemId = getSystemId(); | |
state.RealRollableTable.systemId = systemId; | |
systemChar = getObj('character', state.RealRollableTable.systemId); | |
} | |
// If systemChar is undefined here, it doesn't exist | |
return systemChar ? 'character|' + systemChar.id : 'System'; | |
} | |
function handleInput(msg) { | |
var args, arg0, command, isApi, isHelp; | |
isApi = msg.type === 'api'; | |
args = splitArgs(msg.content.trim()); | |
if (isApi) { | |
// Call !command or help message for !command | |
command = args.shift().substring(1).toLowerCase(); | |
arg0 = args.shift(); | |
if (arg0) { | |
arg0 = arg0.toLowerCase(); | |
} | |
isHelp = arg0 === 'help' || arg0 === 'h'; | |
if (!isHelp) { | |
if (arg0 && arg0.length > 0) { | |
args.unshift(arg0); | |
} | |
if (_.isFunction(commands[command])) { | |
commands[command](args, msg); | |
} | |
} else if (_.isFunction(commands.help)) { | |
commands.help(command, args, msg); | |
} | |
} else if (_.isFunction(commands['msg_' + msg.type])) { | |
// Handle non-api command input | |
commands['msg_' + msg.type](args, msg); | |
} | |
} | |
function checkInstall() { | |
// Initialize default `state` | |
if (!state.RealRollableTable || | |
!state.RealRollableTable.version || | |
state.RealRollableTable.version !== version) { | |
state.RealRollableTable = { | |
version: version, | |
systemId: getSystemId() | |
}; | |
} | |
} | |
function registerEventHandlers() { | |
on('chat:message', handleInput); | |
} | |
return { | |
checkInstall: checkInstall, | |
registerEventHandlers: registerEventHandlers | |
}; | |
}()); | |
on('ready', function() { | |
'use strict'; | |
RealRollableTable.checkInstall(); | |
RealRollableTable.registerEventHandlers(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment