dice.js 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. const diceRegex = () => /\b(\d*)[dD](\d+)\b(?:\s?([+-])\s?(\d+))?/g
  2. class Roll {
  3. constructor(roll) {
  4. if (roll) Object.assign(this, roll)
  5. }
  6. value() {
  7. return this.values.reduce((a, b) => a + b, this.bonus || 0)
  8. }
  9. toString() {
  10. let str = this.values.map(val => `[${val}]`).join(' + ')
  11. if (this.bonus < 0) {
  12. str += ` - ${Math.abs(this.bonus)}`
  13. } else if (this.bonus > 0) {
  14. str += ` + ${this.bonus}`
  15. }
  16. if (this.values.length > 1 || this.bonus) {
  17. str += ` = ${this.value()}`
  18. }
  19. return str
  20. }
  21. }
  22. class BadRoll extends Roll {
  23. constructor(text) {
  24. super({text})
  25. }
  26. toString() {
  27. return this.text
  28. }
  29. }
  30. class Dice {
  31. constructor(dice) {
  32. if (dice) Object.assign(this, dice)
  33. }
  34. roll() {
  35. if (this.count > 100) return new BadRoll(`I lost count rolling ${this}`)
  36. if (this.sides > 256) return new BadRoll(`The faces are too small to read on ${this}`)
  37. if (this.count === 0) return new BadRoll(`I finished before I started rolling ${this}`)
  38. if (this.sides === 0) return new BadRoll(`I lost my grip on ${this}`)
  39. if (this.sides === 1) return new BadRoll(`The mobius ${this} never stopped rolling`)
  40. if (this.bonusSign === '-' && this.bonus > this.sides) return new BadRoll(`It doesn't seem fair to roll ${this}`)
  41. let values = []
  42. for (let i = 0; i < this.count; i++) {
  43. values.push(Math.floor(Math.random() * this.sides) + 1)
  44. }
  45. let bonus = 0
  46. switch (this.bonusSign) {
  47. case '-': bonus = -this.bonus; break
  48. case '+': bonus = this.bonus; break
  49. }
  50. return new Roll({
  51. values,
  52. bonus
  53. })
  54. }
  55. toString() {
  56. let str = `${this.count}d${this.sides}`
  57. if (this.bonusSign) {
  58. str += this.bonusSign + this.bonus
  59. }
  60. return str
  61. }
  62. }
  63. Dice.parse = str => {
  64. const regex = diceRegex()
  65. const ret = []
  66. let dice
  67. while (dice = regex.exec(str)) {
  68. ret.push(new Dice({
  69. match: dice,
  70. count: dice[1] === '' ? 1 : parseFloat(dice[1]),
  71. sides: parseFloat(dice[2]),
  72. bonusSign: dice[3] || null,
  73. bonus: dice[4] === undefined ? null : parseFloat(dice[4])
  74. }))
  75. }
  76. return ret
  77. }
  78. Dice.chat = chat => {
  79. const dice = Dice.parse(chat)
  80. if (dice.length) {
  81. const diceStrings = dice.map(x => x.toString())
  82. if (diceStrings.length > 1) {
  83. diceStrings[diceStrings.length - 1] = `and ${diceStrings[diceStrings.length - 1]}`
  84. }
  85. const rollingString = `Rolling ${diceStrings.join(', ')}...`
  86. const rollsStrings = dice.map(die => die.roll().toString())
  87. return `${rollingString} ${rollsStrings.join(', ')}.`
  88. }
  89. }
  90. module.exports = {Dice, Roll}