All files roll.js

100% Statements 30/30
100% Branches 18/18
100% Functions 1/1
100% Lines 27/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110                                              1x                                   6x         6x         6x         6x             6x 1x   6x 1x     6x 10x 10x 9x       6x             9x 11x 11x 11x             6x 6x 2x   4x   6x 5x   1x 1x 1x        
/**
 * Roll a die or multiple dice.
 * <pre>
 *      This command will roll dice and return the results. A few flows to consider:
 *      - number of dice and the number of dice faces delimited on a 'd'
 *          - delimiter can be capital or lower case;
 *      - multiple dice delimited by a space( ), comma(,), or plus sign(+)
 *      - space delimiting different dice rolls
 *      - multiple types of dice
 *      - multiple dice in roll
 *      - combinations of above
 * <pre>
 * Example Usage and Results:
 * <table style="width:30%;">
 *  <tr><td>Roll Argument(s)</td><td>Result Range</td><td>Example Output</td></tr>
 *  <tr><td>1d6</td><td>number between 1 and 6</td><td>4</td></tr>
 *  <tr><td>2d6</td><td>number between 2 and 12</td><td>6 = 4 + 2</td></tr>
 *  <tr><td>1d6 + 1d4</td><td>number between 2 and 10</td><td>4 = 1 + 3</td></tr>
 * </table> 
 * @module roll 
 * @param {...string} dice  - which dice (number of dice and/or type of dice) to roll; required
 * @return {string} - dice results (as a message to discord text channel)
 */
 module.exports = {
    name: 'roll',
    description: 'Roll a die or multiple dice!',
    aliases: ['dice'],
    usage: '#dType, ex: 1d6, 2d8, 1d6 + 1d12, 1d4+1d6, 1d4 1d12',
    args: true,
    cooldown: 0.1,
    /**
     * @method execute
     * @param {string} message - command, used to determine which channel to return results
     * @param {string} args - dice roll passed into arguments
     * @return {string} total of the die or dice rolled (includes each die roll if multiple dice requested) 
     */
    execute(message,args) {   
        /** 
         * @var {integer} dieTotal  
         * @summary total die roll, starting at 0 and incremented up based on die roll
         */
        let dieTotal = 0;
        /**
         * @var {string[]} dieArray 
         * @summary where we will prep and store the dice roll 
         */
        let dieArray = [];
        /**
         * @var {integer[]} diceArray
         * @summary results of each dice result for additional info in the roll 
         */
        let diceArray = [];
        /**
         * @var {string[]} userRoll
         * @summary the user input for the roll command, empty array if undefined
         */
        let userRoll =  args !== undefined ? args : []; 
        /**
         * * check if its one argument (no spaces) and has a plus (multiple dice)
         * if 0 args, program will fail since args are requires
         * if 1 arg, check if its a 1 dice roll or multipled dice without whitespace
         * if it is missing spaces, tell the user and exit the function early
         */
        if (userRoll.length === 1 && userRoll[0].indexOf('+') > -1) {
            userRoll = userRoll[0].split('+');
        }        
        if (userRoll.length === 1 && userRoll[0].indexOf(',') > -1) {
            userRoll = userRoll[0].split(',');
        }        
        // grab the raw input and remove bad array values/split them/etc.
        for (let i = 0; i < userRoll.length; i++) {
            let roll = userRoll[i].toLowerCase().split('d');
            if (!roll.includes('+')) {
                dieArray.push(roll);
            }
        }
        // roll each set of dice and then increment our total based on the results
        for (let j = 0; j < dieArray.length; j++) {
            /**
             * Roll each die independently, i.e. a 2d12 can be (1-12) + (1-12), not 2 * (1-12)
             *! NOTE: I am absolutely not a fan of a nested for loop method, but I want to ensure each die is rolled independently
             * Unless someone is rolling an absolutely insane amount of similar dice AND 
             * multiple types of dice combos, it shouldn't even be noticeable
             */
            for (let k = 0; k < parseInt(dieArray[j][0]); k++) {
                let roll = Math.ceil(Math.random() * parseInt(dieArray[j][1]));
                diceArray.push(roll);
                dieTotal += roll;
            }
        }
        /**
         * Clean up the final results and return them in a message
         *! Note: We add each die roll into the string if multiple are called in order to provide transparency in the rolls 
         */
        let total = "";
        if (diceArray.length <= 1) {
            total = '> ' + dieTotal.toString();
        } else {
            total = '> ' + dieTotal.toString() + ' = ' + diceArray.join(' + ');
        }
        if ((!total.includes("NaN") && !total.includes("> 0"))) {       // strings parse to 0 above, so this catches it as an error
            message.channel.send(total);
        } else {
            let reply = `You haven't used the roll command correctly, ${message.author}!`;
            reply += `\nThe correct usage looks like ${module.exports.usage}`;
            message.channel.send(reply);
        }
    }
};