dice_concise for IP.Chat

A concise dice simulator for IP.Chat.

  1. // ==UserScript==
  2. // @name dice_concise for IP.Chat
  3. // @namespace Makaze
  4. // @include *
  5. // @grant none
  6. // @version 0.1.9
  7. // @description A concise dice simulator for IP.Chat.
  8. // ==/UserScript==
  9.  
  10. // dice_concise / Based on Marcel Kossin's 'dice' RP Dice Simulator
  11. //
  12. // What is this?
  13. //
  14. // -- Marcel Kossin's notes: --
  15. //
  16. // I (mkossin) often Dungeon Master on our Neverwinternights Servers called 'Bund der
  17. // alten Reiche' (eng. 'Alliance of the old realms') at bundderaltenreiche.de
  18. // (German Site) Often idling in our Channel I thought it might be Fun to have
  19. // a script to dice. Since I found nothing for irssi I wrote this little piece
  20. // of script. The script assumes, that if a 'd' for english dice is given it
  21. // should print the output in English. On the other hand if a 'w' for German
  22. // 'Würfel' is given it prints the output in German.
  23. //
  24. // Usage.
  25. //
  26. // Anyone on the Channel kann ask '!roll' to toss the dice for him. He just has
  27. // to say what dice he want to use. The notation should be well known from
  28. // RP :-) Thus
  29. //
  30. // Write: !roll <quantity of dice>d[or w for german users]<sides on dice>
  31. //
  32. // Here are some examples
  33. //
  34. // !roll 2d20
  35. // !roll 3d6
  36. //
  37. // OK, I think you got it already :-)
  38. //
  39. // Write: !roll version
  40. // For Version Information
  41. //
  42. // Write: !roll help
  43. // For Information about how to use it
  44. //
  45. // -- Makaze's notes: --
  46. //
  47. // [Changes in dice_concise:]
  48. //
  49. // Features added:
  50. //
  51. // [ ] Can add bonuses to the roll. e.g. "!roll 3d6+10"
  52. // [ ] Output changed to one line only. e.g. "Makaze rolls the 3d6 and gets: 9 [4,
  53. // 4, 1]"
  54. // [ ] Corrected English grammar.
  55. // [ ] Removed insults.
  56. // [ ] Cleaner code with fewer nested if statements and true case switches.
  57. // [ ] Errors call before the loop, saving clock cycles.
  58. //
  59. // Bugs fixed:
  60. //
  61. // [ ] Rolls within the correct range.*
  62. //
  63. // Edge cases added:
  64. //
  65. // [ ] Catch if rolling less than 1 dice.
  66. // [ ] Catch if dice or sides are above 100 instead of 99.
  67. //
  68. // -----------------------------------------
  69. //
  70. // * [The original dice.pl rolled a number between 1 and (<number of sides> - 1)]
  71. // [instead of using the full range. e.g. "!roll 1d6" would output 1 through ]
  72. // [5, but never 6. ]
  73. //
  74. // -----------------------------------------
  75. //
  76. // Original script 'dice.pl' by mkossin.
  77. //
  78. // Updated and ported script 'dice_concise.js' by Makaze.
  79.  
  80. // Classes constructor
  81.  
  82. function ClassHandler() {
  83. var self = this;
  84.  
  85. this.classList = function(elem) {
  86. return elem.className.trim().split(/[\b\s]/);
  87. };
  88.  
  89. this.hasClass = function(elem, className) {
  90. var classes = self.classList(elem),
  91. has = false,
  92. i = 0;
  93.  
  94. for (i = 0; i < classes.length; i++) {
  95. if (classes[i] === className) {
  96. has = true;
  97. break;
  98. }
  99. }
  100.  
  101. return (has);
  102. };
  103.  
  104. this.addClass = function(elem, className) {
  105. var classes;
  106.  
  107. if (!self.hasClass(elem, className)) {
  108. classes = self.classList(elem);
  109. classes.push(className);
  110. elem.className = classes.join(' ').trim();
  111. }
  112.  
  113. return self;
  114. };
  115.  
  116. this.removeClass = function(elem, className) {
  117. var classes = self.classList(elem),
  118. i = 0;
  119.  
  120. for (i = 0; i < classes.length; i++) {
  121. if (classes[i] === className) {
  122. classes.splice(i, 1);
  123. }
  124. }
  125.  
  126. elem.className = classes.join(' ').trim();
  127.  
  128. return self;
  129. };
  130.  
  131. this.toggleClass = function(elem, className) {
  132. var classes;
  133.  
  134. if (self.hasClass(elem, className)) {
  135. self.removeClass(elem, className);
  136. } else {
  137. classes = self.classList(elem);
  138. classes.push(className);
  139. elem.className = classes.join(' ').trim();
  140. }
  141.  
  142. return self;
  143. };
  144. }
  145.  
  146. // Initialize
  147.  
  148. var Classes = new ClassHandler();
  149.  
  150. // End Classes constructor
  151.  
  152. function pushMsg(msg) {
  153. var keep = input.value;
  154. input.value = msg;
  155. submit.click();
  156. if (keep.length) {
  157. input.value = keep;
  158. }
  159. }
  160.  
  161. function rand(min, max) {
  162. return Math.floor(Math.random() * (max - min + 1) + min);
  163. }
  164.  
  165. if (document.body.id == 'ipboard_body' && document.getElementById('chat-form') != null) {
  166. var NAME = 'dice_concise',
  167. VERSION = '0.1.9';
  168.  
  169. var latestMessage,
  170. latestMessageTxt,
  171. nick,
  172. curr,
  173. opts = (localStorage.getItem('MakazeScriptOptions')) ? JSON.parse(localStorage.getItem('MakazeScriptOptions')) : {},
  174. key = (opts.hasOwnProperty('dice_concise_key')) ? opts.dice_concise_key.toUpperCase() : '!ROLL';
  175.  
  176. console.log(NAME, VERSION, 'loaded.');
  177.  
  178. var input = document.getElementById('message_textarea');
  179. var submit = document.getElementById('chat-submit');
  180.  
  181. document.addEventListener('DOMNodeInserted', function(event) {
  182. if (event.target.nodeType !== 1 || event.target.id !== 'storage_chatroom') {
  183. return false;
  184. }
  185.  
  186. latestMessage = event.target.parentNode.getElementsByTagName('div')[event.target.parentNode.getElementsByTagName('div').length - 1];
  187.  
  188. if (!Classes.hasClass(latestMessage.parentNode, 'post')) {
  189. return false;
  190. }
  191.  
  192. if (!Classes.hasClass(latestMessage.parentNode, 'chat-message')) {
  193. return false;
  194. }
  195.  
  196. latestMessageTxt = latestMessage.textContent;
  197. if (!latestMessageTxt.length) {
  198. return false;
  199. }
  200. if (latestMessageTxt.substr(0, key.length).toLowerCase() !== key.toLowerCase()) {
  201. return false;
  202. }
  203.  
  204. curr = latestMessage.parentNode;
  205. nick = null;
  206.  
  207. while (nick === null) {
  208. if (curr.getElementsByTagName('label').length) {
  209. nick = curr.getElementsByTagName('label')[0].textContent;
  210. } else {
  211. curr = curr.previousSibling;
  212. }
  213. }
  214.  
  215. if (latestMessageTxt.match(/\d[dw]\d/i)) {
  216. var rnd,
  217. forloop = 0,
  218. lang,
  219. roll = latestMessageTxt.split(/\s/)[1],
  220. values = roll.split(/[dw\+\-\*\/]/i),
  221. dice = parseInt(values[0]),
  222. sides = parseInt(values[1]),
  223. modifiers = roll.match(/[\+\-\*\/]\d+/g),
  224. modifyType,
  225. modifyVal,
  226. modifyErrors = roll.match(/[\+\-\*\/][^\d\+\-\*\/]+/g),
  227. value = 0,
  228. // Modifier support added
  229. rolls = [];
  230.  
  231. if (roll.match(/\d[w]\d//i)) {
  232. lang = 'DE';
  233. } else {
  234. lang = 'EN';
  235. }
  236.  
  237. if (dice < 1) {
  238. switch (lang) {
  239. case 'DE':
  240. pushMsg('/me [b]' + nick + '[/b] macht nichts... Würfeln funktioniert am besten mit Würfeln.');
  241. break;
  242. case 'EN':
  243. default:
  244. pushMsg('/me [b]' + nick + '[/b] does nothing... Rolling dice works best with dice.');
  245. break;
  246. }
  247. return false;
  248. } else if (dice > 100) {
  249. switch (lang) {
  250. case 'DE':
  251. pushMsg('/me [b]' + nick + '[/b] scheitert den ' + roll + ' zu werfen... Versuch es mit weniger Würfeln.');
  252. break;
  253. case 'EN':
  254. default:
  255. pushMsg('/me [b]' + nick + '[/b] fails to roll the ' + roll + '... Try fewer dice.');
  256. break;
  257. }
  258. return false;
  259. } else if (sides <= 1) {
  260. if (sides == 0) {
  261. switch (lang) {
  262. case 'DE':
  263. pushMsg('/me [b]' + nick + '[/b] verursacht ein Paradox... Oder hat jemand schon mal einen Würfel ohne Seiten gesehen?');
  264. break;
  265. case 'EN':
  266. default:
  267. pushMsg('/me [b]' + nick + '[/b] causes a paradox... Or has anybody ever seen a die without sides?');
  268. break;
  269. }
  270. return false;
  271. } else if (sides == 1) {
  272. switch (lang) {
  273. case 'DE':
  274. pushMsg('/me [b]' + nick + '[/b] verursacht ein Paradox... Oder hat jemand schon mal einen Würfel mit nur einer Seite gesehen?');
  275. break;
  276. case 'EN':
  277. default:
  278. pushMsg('/me [b]' + nick + '[/b] causes a paradox... Or has anybody ever seen a die with only one side?');
  279. break;
  280. }
  281. return false;
  282. }
  283. } else if (sides > 100) {
  284. switch (lang) {
  285. case 'DE':
  286. pushMsg('/me [b]' + nick + '[/b] scheitert den ' + roll + ' zu werfen... Versuch es mit weniger Augen.');
  287. break;
  288. case 'EN':
  289. default:
  290. pushMsg('/me [b]' + nick + '[/b] fails to roll the ' + roll + '... Try fewer sides.');
  291. break;
  292. }
  293. return false;
  294. }
  295. for (forloop = 0; forloop < dice; forloop++) {
  296. rnd = rand(1, sides);
  297. value += rnd;
  298. rolls[forloop] = rnd;
  299. }
  300. if (modifiers) {
  301. for (var i = 0; i < modifiers.length; i++) {
  302. modifyType = modifiers[i].match(/[\+\-\*\/]/)[0];
  303. modifyVal = parseInt(modifiers[i].match(/\d+/)[0]);
  304. switch (modifyType) {
  305. case '*':
  306. value = value * modifyVal;
  307. break;
  308. case '/':
  309. value = value / modifyVal;
  310. break;
  311. case '+':
  312. value = value + modifyVal;
  313. break;
  314. case '-':
  315. value = value - modifyVal;
  316. break;
  317. }
  318. }
  319. }
  320. switch (lang) {
  321. case 'DE':
  322. pushMsg('/me [b]' + nick + '[/b] würfelt mit dem ' + roll + ' und erhält: ' + value + ' [' + rolls.join(', ') + ']');
  323. break;
  324. case 'EN':
  325. default:
  326. pushMsg('/me [b]' + nick + '[/b] rolls the ' + roll + ' and gets: ' + value + ' [' + rolls.join(', ') + ']');
  327. break;
  328. }
  329. if (modifyErrors) {
  330. switch (lang) {
  331. case 'DE':
  332. pushMsg('/me [b]' + nick + '[/b] scheitert ihr Ergebnis zu ändern. Versuch es mit Zahlen. [' + modifyErrors.join(', ') + ']');
  333. break;
  334. case 'EN':
  335. default:
  336. pushMsg('/me [b]' + nick + '[/b] fails to modify their result. Try using numbers. [' + modifyErrors.join(', ') + ']');
  337. break;
  338. }
  339. }
  340. return true;
  341. } else if (latestMessageTxt.substr(0, key.length + 1 + 7).toLowerCase() === key.toLowerCase() + ' version') {
  342. pushMsg('/me [b]' + NAME + '[/b]: Version ' + VERSION + ' for IP.Chat by [b]Makaze[/b] [inspired by [b]mkossin[/b]]');
  343. return false;
  344. } else if (latestMessageTxt.substr(0, key.length + 1 + 4).toLowerCase() === key.toLowerCase() + ' help') {
  345. pushMsg('/me [b]Syntax[/b]: "!roll <quantity of dice>d<sides on dice>[<+-*/>modifier]" - e.g. "!roll 2d20", "!roll 2d20*2+10"');
  346. return false;
  347. } else if (latestMessageTxt.substr(0, key.length + 1 + 5).toLowerCase() === key.toLowerCase() + ' hilfe') {
  348. pushMsg('/me [b]Syntax[/b]: "!roll <Anzahl der Würfel>w<Augen des Würfels>[<+-*/>Modifikator]" - z.B. "!roll 2w20", "!roll 2w20*2+10"');
  349. return false;
  350. } else {
  351. pushMsg(
  352. '/me "!roll help" - gives the English help' +
  353. '\n' +
  354. '"!roll hilfe" - zeigt die deutsche Hilfe an'
  355. );
  356. return false;
  357. }
  358. });
  359. }