// ==UserScript==
// @name dice_concise for IP.Chat
// @namespace Makaze
// @include *
// @grant none
// @version 0.1.9
// @description A concise dice simulator for IP.Chat.
// ==/UserScript==
// dice_concise / Based on Marcel Kossin's 'dice' RP Dice Simulator
//
// What is this?
//
// -- Marcel Kossin's notes: --
//
// I (mkossin) often Dungeon Master on our Neverwinternights Servers called 'Bund der
// alten Reiche' (eng. 'Alliance of the old realms') at bundderaltenreiche.de
// (German Site) Often idling in our Channel I thought it might be Fun to have
// a script to dice. Since I found nothing for irssi I wrote this little piece
// of script. The script assumes, that if a 'd' for english dice is given it
// should print the output in English. On the other hand if a 'w' for German
// 'Würfel' is given it prints the output in German.
//
// Usage.
//
// Anyone on the Channel kann ask '!roll' to toss the dice for him. He just has
// to say what dice he want to use. The notation should be well known from
// RP :-) Thus
//
// Write: !roll <quantity of dice>d[or w for german users]<sides on dice>
//
// Here are some examples
//
// !roll 2d20
// !roll 3d6
//
// OK, I think you got it already :-)
//
// Write: !roll version
// For Version Information
//
// Write: !roll help
// For Information about how to use it
//
// -- Makaze's notes: --
//
// [Changes in dice_concise:]
//
// Features added:
//
// [ ] Can add bonuses to the roll. e.g. "!roll 3d6+10"
// [ ] Output changed to one line only. e.g. "Makaze rolls the 3d6 and gets: 9 [4,
// 4, 1]"
// [ ] Corrected English grammar.
// [ ] Removed insults.
// [ ] Cleaner code with fewer nested if statements and true case switches.
// [ ] Errors call before the loop, saving clock cycles.
//
// Bugs fixed:
//
// [ ] Rolls within the correct range.*
//
// Edge cases added:
//
// [ ] Catch if rolling less than 1 dice.
// [ ] Catch if dice or sides are above 100 instead of 99.
//
// -----------------------------------------
//
// * [The original dice.pl rolled a number between 1 and (<number of sides> - 1)]
// [instead of using the full range. e.g. "!roll 1d6" would output 1 through ]
// [5, but never 6. ]
//
// -----------------------------------------
//
// Original script 'dice.pl' by mkossin.
//
// Updated and ported script 'dice_concise.js' by Makaze.
// Classes constructor
function ClassHandler() {
var self = this;
this.classList = function(elem) {
return elem.className.trim().split(/[\b\s]/);
};
this.hasClass = function(elem, className) {
var classes = self.classList(elem),
has = false,
i = 0;
for (i = 0; i < classes.length; i++) {
if (classes[i] === className) {
has = true;
break;
}
}
return (has);
};
this.addClass = function(elem, className) {
var classes;
if (!self.hasClass(elem, className)) {
classes = self.classList(elem);
classes.push(className);
elem.className = classes.join(' ').trim();
}
return self;
};
this.removeClass = function(elem, className) {
var classes = self.classList(elem),
i = 0;
for (i = 0; i < classes.length; i++) {
if (classes[i] === className) {
classes.splice(i, 1);
}
}
elem.className = classes.join(' ').trim();
return self;
};
this.toggleClass = function(elem, className) {
var classes;
if (self.hasClass(elem, className)) {
self.removeClass(elem, className);
} else {
classes = self.classList(elem);
classes.push(className);
elem.className = classes.join(' ').trim();
}
return self;
};
}
// Initialize
var Classes = new ClassHandler();
// End Classes constructor
function pushMsg(msg) {
var keep = input.value;
input.value = msg;
submit.click();
if (keep.length) {
input.value = keep;
}
}
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
if (document.body.id == 'ipboard_body' && document.getElementById('chat-form') != null) {
var NAME = 'dice_concise',
VERSION = '0.1.9';
var latestMessage,
latestMessageTxt,
nick,
curr,
opts = (localStorage.getItem('MakazeScriptOptions')) ? JSON.parse(localStorage.getItem('MakazeScriptOptions')) : {},
key = (opts.hasOwnProperty('dice_concise_key')) ? opts.dice_concise_key.toUpperCase() : '!ROLL';
console.log(NAME, VERSION, 'loaded.');
var input = document.getElementById('message_textarea');
var submit = document.getElementById('chat-submit');
document.addEventListener('DOMNodeInserted', function(event) {
if (event.target.nodeType !== 1 || event.target.id !== 'storage_chatroom') {
return false;
}
latestMessage = event.target.parentNode.getElementsByTagName('div')[event.target.parentNode.getElementsByTagName('div').length - 1];
if (!Classes.hasClass(latestMessage.parentNode, 'post')) {
return false;
}
if (!Classes.hasClass(latestMessage.parentNode, 'chat-message')) {
return false;
}
latestMessageTxt = latestMessage.textContent;
if (!latestMessageTxt.length) {
return false;
}
if (latestMessageTxt.substr(0, key.length).toLowerCase() !== key.toLowerCase()) {
return false;
}
curr = latestMessage.parentNode;
nick = null;
while (nick === null) {
if (curr.getElementsByTagName('label').length) {
nick = curr.getElementsByTagName('label')[0].textContent;
} else {
curr = curr.previousSibling;
}
}
if (latestMessageTxt.match(/\d[dw]\d/i)) {
var rnd,
forloop = 0,
lang,
roll = latestMessageTxt.split(/\s/)[1],
values = roll.split(/[dw\+\-\*\/]/i),
dice = parseInt(values[0]),
sides = parseInt(values[1]),
modifiers = roll.match(/[\+\-\*\/]\d+/g),
modifyType,
modifyVal,
modifyErrors = roll.match(/[\+\-\*\/][^\d\+\-\*\/]+/g),
value = 0,
// Modifier support added
rolls = [];
if (roll.match(/\d[w]\d//i)) {
lang = 'DE';
} else {
lang = 'EN';
}
if (dice < 1) {
switch (lang) {
case 'DE':
pushMsg('/me [b]' + nick + '[/b] macht nichts... Würfeln funktioniert am besten mit Würfeln.');
break;
case 'EN':
default:
pushMsg('/me [b]' + nick + '[/b] does nothing... Rolling dice works best with dice.');
break;
}
return false;
} else if (dice > 100) {
switch (lang) {
case 'DE':
pushMsg('/me [b]' + nick + '[/b] scheitert den ' + roll + ' zu werfen... Versuch es mit weniger Würfeln.');
break;
case 'EN':
default:
pushMsg('/me [b]' + nick + '[/b] fails to roll the ' + roll + '... Try fewer dice.');
break;
}
return false;
} else if (sides <= 1) {
if (sides == 0) {
switch (lang) {
case 'DE':
pushMsg('/me [b]' + nick + '[/b] verursacht ein Paradox... Oder hat jemand schon mal einen Würfel ohne Seiten gesehen?');
break;
case 'EN':
default:
pushMsg('/me [b]' + nick + '[/b] causes a paradox... Or has anybody ever seen a die without sides?');
break;
}
return false;
} else if (sides == 1) {
switch (lang) {
case 'DE':
pushMsg('/me [b]' + nick + '[/b] verursacht ein Paradox... Oder hat jemand schon mal einen Würfel mit nur einer Seite gesehen?');
break;
case 'EN':
default:
pushMsg('/me [b]' + nick + '[/b] causes a paradox... Or has anybody ever seen a die with only one side?');
break;
}
return false;
}
} else if (sides > 100) {
switch (lang) {
case 'DE':
pushMsg('/me [b]' + nick + '[/b] scheitert den ' + roll + ' zu werfen... Versuch es mit weniger Augen.');
break;
case 'EN':
default:
pushMsg('/me [b]' + nick + '[/b] fails to roll the ' + roll + '... Try fewer sides.');
break;
}
return false;
}
for (forloop = 0; forloop < dice; forloop++) {
rnd = rand(1, sides);
value += rnd;
rolls[forloop] = rnd;
}
if (modifiers) {
for (var i = 0; i < modifiers.length; i++) {
modifyType = modifiers[i].match(/[\+\-\*\/]/)[0];
modifyVal = parseInt(modifiers[i].match(/\d+/)[0]);
switch (modifyType) {
case '*':
value = value * modifyVal;
break;
case '/':
value = value / modifyVal;
break;
case '+':
value = value + modifyVal;
break;
case '-':
value = value - modifyVal;
break;
}
}
}
switch (lang) {
case 'DE':
pushMsg('/me [b]' + nick + '[/b] würfelt mit dem ' + roll + ' und erhält: ' + value + ' [' + rolls.join(', ') + ']');
break;
case 'EN':
default:
pushMsg('/me [b]' + nick + '[/b] rolls the ' + roll + ' and gets: ' + value + ' [' + rolls.join(', ') + ']');
break;
}
if (modifyErrors) {
switch (lang) {
case 'DE':
pushMsg('/me [b]' + nick + '[/b] scheitert ihr Ergebnis zu ändern. Versuch es mit Zahlen. [' + modifyErrors.join(', ') + ']');
break;
case 'EN':
default:
pushMsg('/me [b]' + nick + '[/b] fails to modify their result. Try using numbers. [' + modifyErrors.join(', ') + ']');
break;
}
}
return true;
} else if (latestMessageTxt.substr(0, key.length + 1 + 7).toLowerCase() === key.toLowerCase() + ' version') {
pushMsg('/me [b]' + NAME + '[/b]: Version ' + VERSION + ' for IP.Chat by [b]Makaze[/b] [inspired by [b]mkossin[/b]]');
return false;
} else if (latestMessageTxt.substr(0, key.length + 1 + 4).toLowerCase() === key.toLowerCase() + ' help') {
pushMsg('/me [b]Syntax[/b]: "!roll <quantity of dice>d<sides on dice>[<+-*/>modifier]" - e.g. "!roll 2d20", "!roll 2d20*2+10"');
return false;
} else if (latestMessageTxt.substr(0, key.length + 1 + 5).toLowerCase() === key.toLowerCase() + ' hilfe') {
pushMsg('/me [b]Syntax[/b]: "!roll <Anzahl der Würfel>w<Augen des Würfels>[<+-*/>Modifikator]" - z.B. "!roll 2w20", "!roll 2w20*2+10"');
return false;
} else {
pushMsg(
'/me "!roll help" - gives the English help' +
'\n' +
'"!roll hilfe" - zeigt die deutsche Hilfe an'
);
return false;
}
});
}