Explorar o código

Advantage and rerolls

Alan Colon %!s(int64=7) %!d(string=hai) anos
pai
achega
83a5045721
Modificáronse 1 ficheiros con 116 adicións e 4 borrados
  1. 116 4
      dice.js

+ 116 - 4
dice.js

@@ -1,4 +1,7 @@
 const diceRegex = () => /\b(\d*)[dD](\d+)\b(?:\s?([+-])\s?(\d+))?/g
+const rerollRegex = () => /\b(reroll|re-roll)\s+((?:(?:\S*|\d+s|\d+'s|\d+)\s*)+)/
+const numberRegex = () => /\b(one|two|three|four|five|six|seven|eight|nine|ten|ones|twos|threes|fours|fives|sixes|sevens|eights|nines|tens|one's|two's|three's|four's|five's|seven's|eight's|nine's|ten's|\d+)\b/gi
+const advRegex = () => /(disadvantage|advantage)/i
 
 class Roll {
   constructor(roll) {
@@ -17,6 +20,11 @@ class Roll {
     if (this.values.length > 1 || this.bonus) {
       str += ` = ${this.value()}`
     }
+    if (this.rerolls === 1) {
+      str += ` with 1 reroll`
+    } else if (this.rerolls > 1) {
+      str += ` with ${this.rerolls} rerolls`
+    }
     return str
   }
 }
@@ -30,6 +38,8 @@ class BadRoll extends Roll {
   }
 }
 
+const random = n => Math.floor(Math.random() * n) + 1
+
 class Dice {
   constructor(dice) {
     if (dice) Object.assign(this, dice)
@@ -44,16 +54,36 @@ class Dice {
 
     let values = []
     for (let i = 0; i < this.count; i++) {
-      values.push(Math.floor(Math.random() * this.sides) + 1)
+      if (this.modifier === 'advantage') {
+        values.push(Math.max(random(this.sides), random(this.sides)))
+      } else if (this.modifier === 'disadvantage') {
+        values.push(Math.min(random(this.sides), random(this.sides)))
+      } else {
+        values.push(random(this.sides))
+      }
     }
     let bonus = 0
     switch (this.bonusSign) {
       case '-': bonus = -this.bonus; break
       case '+': bonus = this.bonus; break
     }
+    let rerolls = 0
+    for (let i = 0; i < 10; i++) {
+      let foundAny = false
+      for (let d = 0; d < values.length; d++) {
+        const v = values[d]
+        if (this.rerollNumbers && this.rerollNumbers.includes(v)) {
+          values[d] = random(this.sides)
+          foundAny = true
+          rerolls++
+        }
+      }
+      if (!foundAny) break
+    }
     return new Roll({
       values,
-      bonus
+      bonus,
+      rerolls
     })
   }
   toString() {
@@ -61,11 +91,85 @@ class Dice {
     if (this.bonusSign) {
       str += this.bonusSign + this.bonus
     }
+    if (this.modifier) {
+      str += ` with ${this.modifier}`
+    }
+    if (this.rerollNumbers && this.rerollNumbers.length) {
+      if (this.rerollNumbers.length === 1) {
+        str += ` and reroll ${this.rerollNumbers[0]}'s`
+      } else {
+        str += ` and reroll ${this.rerollNumbers.slice(0, this.rerollNumbers.length - 1).join(`'s, `)}'s, and ${this.rerollNumbers[this.rerollNumbers.length - 1]}'s`
+      }
+    }
     return str
   }
 }
 
 Dice.parse = str => {
+  const rrRegex = rerollRegex()
+  const rerolls = []
+  const reroll = rrRegex.exec(str)
+  const rerollNumbers = []
+  if (reroll) {
+    const nRegex = numberRegex()
+    while (number = nRegex.exec(str)) {
+      switch (number[1]) {
+        case 'ones':
+        case 'one':
+          rerollNumbers.push(1)
+          break
+        case 'twos':
+        case 'two':
+          rerollNumbers.push(2)
+          break
+        case 'threes':
+        case 'three':
+          rerollNumbers.push(3)
+          break
+        case 'fours':
+        case 'four':
+          rerollNumbers.push(4)
+          break
+        case 'fives':
+        case 'five':
+          rerollNumbers.push(5)
+          break
+        case 'sixes':
+        case 'six':
+          rerollNumbers.push(6)
+          break
+        case 'sevens':
+        case 'seven':
+          rerollNumbers.push(7)
+          break
+        case 'eights':
+        case 'eight':
+          rerollNumbers.push(8)
+          break
+        case 'nines':
+        case 'nine':
+          rerollNumbers.push(9)
+          break
+        case 'tens':
+        case 'ten':
+          rerollNumbers.push(10)
+          break
+        default:
+          const n = parseFloat(number)
+          if (!isNaN(n)) {
+            rerollNumbers.push(n)
+          }
+      }
+    }
+  }
+
+  const aRegex = advRegex()
+  const ad = aRegex.exec(str)
+  
+  const modifier = ad ? (
+    ad[1].toLowerCase()[0] === 'a' ? 'advantage' : 'disadvantage'
+  ) : null
+
   const regex = diceRegex()
   const ret = []
   let dice
@@ -75,7 +179,9 @@ Dice.parse = str => {
       count: dice[1] === '' ? 1 : parseFloat(dice[1]),
       sides: parseFloat(dice[2]),
       bonusSign: dice[3] || null,
-      bonus: dice[4] === undefined ? null : parseFloat(dice[4])
+      bonus: dice[4] === undefined ? null : parseFloat(dice[4]),
+      rerollNumbers,
+      modifier
     }))
   }
   return ret
@@ -94,4 +200,10 @@ Dice.chat = chat => {
   }
 }
 
-module.exports = {Dice, Roll}
+module.exports = {
+  Dice,
+  Roll,
+  diceRegex,
+  rerollRegex,
+  advRegex
+}