Spice Dice
Author: brooklynsinclair
Description Launch Bot Current Users

Short Description:

Dice game - modified for my room.

Full Description

An (ongoing) modified version of A Bettter Dice Roller by aety for my room.

Just search A Bettter Dice Roller or aety above to find the original bot ;)

*Note: NO changes were made to the integrity of game functions. Removed unused blurbs, customized notices/titles to my liking, & cleaned up launch interface.

Latest test: 11.17.23

ORIGINAL OPEN SOURCE:

/**
* A Bettter Dice Roller by aety
* Version: 1.3.7
* Author: aety
* Date: 2021-03-08
* Loosely based on work by: kingchris_, zingknaat, milo_bloom, tablesalt90
* assuming anonymous tipping is implemented the way I expect this should add support for anonymous tipping
*/

cb.settings_choices = [
{name: 'settings_section_label', type:'choice', label:'################# Basic Settings #################', required: false},
{
name: 'tokens', type: 'int', minValue: 1,
label: 'How much do you want to charge per roll?',
defaultValue: 33
},
{
name: 'notice_wait_time',
type: 'int',
label: 'In minutes, how often should the bot advertise itself? (choosing 0 will never show the timer bot notices)',
minValue: 0,
defaultValue: 10
},
{
name: 'blurb_size',
type: 'choice',
label: 'Size of the timed bot advertisement blurb.',
choice1: 'Minimal Blurb',
choice2: 'Short Blurb',
choice3: 'Without "all" or Bot Name',
choice4: 'Full Blurb',
choice5: 'Off',
defaultValue: 'Without "all" or Bot Name'
},
{
name: 'entry_blurb_size',
type: 'choice',
label: 'Size of the user entry advertisement blurb.',
choice1: 'Minimal Blurb',
choice2: 'Short Blurb',
choice3: 'Without "all" or Bot Name',
choice4: 'Full Blurb',
choice5: 'Off',
defaultValue: 'Full Blurb'
},
{
name: 'change_room_subject', type: 'choice', label: 'Change room subject when using this bot?',
choice1: 'Yes', choice2: 'No', defaultValue: 'No'
},
{name: 'multirolls_subsection_label', type:'choice', label:'######### Multi Rolls #########', required: false},
{
name: 'max_rolls_at_once', type: 'int', minValue: 1,
label: 'Maximum number of rolls that can be tipped for in a single tip.',
defaultValue: 3
},
{
name: 'perfect_multi', type: 'choice',
label: 'Require exact multiples of tip amount to roll the die? (if you charge 33 per roll, 66 will roll 2x but 69 won\'t)',
choice1: 'Yes', choice2: 'No',
defaultValue: 'Yes'
},
{name: 'prizeodds_subsection_label', type:'choice', label:'######### Prize Odds #########', required: false},
{
name: 'equal_odds', type: 'choice',
label: 'Should all non-rare prizes have equal odds of being rolled? ("No" simulates rolling a pair of dice and "Yes" rolls a single die)',
choice1: 'Yes', choice2: 'No',
defaultValue: 'No'
},
{
name: 'last_prize_rare', type: 'choice',
label: 'Should the last prize in the list be RARE? (RARE prizes will only show up after a minimum number of rolls have been rolled and should be significantly less likely than other prizes)',
choice1: 'Yes', choice2: 'No',
defaultValue: 'Yes'
},
{
name: 'minimum_rolls', type: 'int', minValue: 0,
label: 'Minimum number of rolls that must be rolled before RARE prizes can be won.',
defaultValue: 10
},
{
name: 'rare_chance_mult', type: 'choice',
label: 'Multiplier for the chance of rolling rare prizes. Increase if rare prizes are too uncommon, Decrease if they\'re too common',
choice1: '1/2x', choice2: '3/4x', choice3: '1x', choice4: '1.5x', choice5: '2x', choice6: '4x', choice7: '8x',
defaultValue: '1x'
},
{name: 'prize_section_label', type:'choice', label:'#################### Prizes ####################', required: false},
{name: 'prize_1', type: 'str', label: '(List possible prizes from most common to least.) Prize 1'},
{name: 'prize_2', type: 'str', label: '(if equal odds option is \'Yes\' order doesn\'t matter.) Prize 2'},
{name: 'prize_3', type: 'str', label: '(Fill prize spots in order from top to bottom.) Prize 3', required: false},
{name: 'prize_4', type: 'str', label: '(Leave the remaining fields blank to not use them.) Prize 4', required: false}
];

// 25 because I got tired after uploading 25 gifs ;>_>
// if someone wants to get in touch with me about updating/improving
// or adding more dice gifs feel free to leave a comment etc
// I *should* see it
var maxPrizes = 25;

for(var i=5; i <= maxPrizes; i++) {
cb.settings_choices.push({
name: 'prize_' + i,
type: 'str',
label: 'Prize ' + i,
required: false
});
}

// We're pushing these so that we can put them after the prizes that we want to push with a loop
// there are better ways to deal with and organize this code
cb.settings_choices.push({name: 'miscellaneous_section_label', type:'choice', label:'################ Advanced Settings ################', required: false});
cb.settings_choices.push({name: 'miscellaneous_section_desc_label', type:'choice', label:'##### (Safe to Ignore Everything Below this Point) #####', required: false});
cb.settings_choices.push({
name: 'mod_rolls', type: 'choice',
label: 'Allow mods to use the /diceroll command?',
choice1: 'Yes', choice2: 'No',
defaultValue: 'No'
});
cb.settings_choices.push({name: 'cmd_prefix', type: 'str', minLength: 1, maxLength: 1, label: 'Prefix for bot commands. (Might want to change if you have other bots using the same commands.)', defaultValue: '/'});
cb.settings_choices.push({name: 'die_img_prefix', type: 'str', label: 'Prefix for die gifs ("prefix#" only change if you know what you\'re doing. :80ngenericdie_1 through :80ngenericdie_25)', defaultValue: ':80ngenericdie_'});
cb.settings_choices.push({name: 'miscellaneous_section_colors_label', type:'choice', label:'#################### Colors ####################', required: false});
cb.settings_choices.push({name: 'miscellaneous_section_notice_label', type:'choice', label:'################ Notice ################', required: false});
cb.settings_choices.push({name: 'notice_fg_color', type: 'str', label: 'Bot advertisement text color (hex code for color make sure you include the # in front)', defaultValue: '#15A6B0', required: false});
cb.settings_choices.push({name: 'notice_bg_color', type: 'str', label: 'Bot advertisement background color (hex code for color make sure you include the # in front)', defaultValue: '', required: false});
cb.settings_choices.push({name: 'miscellaneous_section_prizes_label', type:'choice', label:'################ Prizes ################', required: false});
cb.settings_choices.push({name: 'prize_fg_color', type: 'str', label: 'Prize text color (hex code for color make sure you include the # in front)', defaultValue: '#067D00', required: false});
cb.settings_choices.push({name: 'prize_bg_color', type: 'str', label: 'Prize background color (hex code for color make sure you include the # in front)', defaultValue: '#D9FAD7', required: false});
cb.settings_choices.push({name: 'miscellaneous_section_rare_label', type:'choice', label:'############### Rare Prize ##############', required: false});
cb.settings_choices.push({name: 'rare_fg_color', type: 'str', label: 'Rare Prize text color (hex code for color make sure you include the # in front)', defaultValue: '#A805A6', required: false});
cb.settings_choices.push({name: 'rare_bg_color', type: 'str', label: 'Rare Prize background color (hex code for color make sure you include the # in front)', defaultValue: '#FFDBF3', required: false});
cb.settings_choices.push({name: 'miscellaneous_section_info_label', type:'choice', label:'############## Stats and Info ##############', required: false});
cb.settings_choices.push({name: 'miscellaneous_section_info_desc_label', type:'choice', label:'(prizes list, winners, and dice stats etc)', required: false});
cb.settings_choices.push({name: 'info_fg_color', type: 'str', label: 'Prizes and Stats text color (hex code for color make sure you include the # in front)', defaultValue: '#008596', required: false});
cb.settings_choices.push({name: 'info_bg_color', type: 'str', label: 'Prizes and Stats background color (hex code for color make sure you include the # in front)', defaultValue: '#DBFBFF', required: false});
cb.settings_choices.push({name: 'winners_fg_color', type: 'str', label: 'Winners text color (hex code for color make sure you include the # in front)', defaultValue: '#8A4900', required: false});
cb.settings_choices.push({name: 'winners_bg_color', type: 'str', label: 'Winners background color (hex code for color make sure you include the # in front)', defaultValue: '#FFF0DE', required: false});

var rollprice = parseInt(cb.settings.tokens);
var prizeCount = 13;
var equalOdds = cb.settings.equal_odds == 'Yes';
var numberOfDie = equalOdds?1:2;
var die = [
{
die: 1,
label: "One",
sides: Math.floor((equalOdds? ((cb.settings.last_prize_rare == 'Yes')?prizeCount-1:prizeCount) : ((cb.settings.last_prize_rare == 'Yes')?prizeCount:prizeCount+1)/2))
},
{
die: 2,
label: "Two",
sides: Math.ceil((equalOdds? ((cb.settings.last_prize_rare == 'Yes')?prizeCount-1:prizeCount) : ((cb.settings.last_prize_rare == 'Yes')?prizeCount:prizeCount+1)/2))
}
];
var perfectMults = cb.settings.perfect_multi == 'Yes';
var maxRollsPerTip = parseInt(cb.settings.max_rolls_at_once);
var usingRares = cb.settings.last_prize_rare == 'Yes';
var minRollsForRare = parseInt(cb.settings.minimum_rolls);

var rareChance = 1;

switch(cb.settings.rare_chance_mult) {
case '1x':
rareChance = 1;
break;
case '1/2x':
rareChance = 0.5;
break;
case '3/4x':
rareChance = 0.75;
break;
case '1.5x':
rareChance = 1.5;
break;
case '2x':
rareChance = 2;
break;
case '4x':
rareChance = 4;
break;
case '8x':
rareChance = 8;
break;
default:
rareChance = 1;
break;
}

var changeSubject = cb.settings.change_room_subject == 'Yes';
var allow_mod_rolls = cb.settings.mod_rolls == 'Yes';
var cmdPrefix = cb.settings.cmd_prefix;
var diePrefix = cb.settings.die_img_prefix;
var notice_fg = cb.settings.notice_fg_color;
var notice_bg = cb.settings.notice_bg_color;
var lastRoller = '--';
var lastPrizeWon = '--';
var prizes = [];
var stats = {
'tipCounter': 0,
'winners': [],
'rollCounter': 0,
'rollResultCounts': {}
};

cb.onTip(function(tip) {
var tipAmount = parseInt(tip.amount);
stats.tipCounter += tipAmount;
if(tipAmount >= rollprice && (!perfectMults || ((tipAmount <= (maxRollsPerTip*rollprice)) && (tipAmount % rollprice === 0)))) {
var rolls = Math.floor(tipAmount/rollprice);
for(var i = 0; i < rolls; i++) {
var username = "anonymous";
if(!tip.is_anon_tip) {
username = tip.from_user;
}
roll(username, true);
lastRoller = username;
}
} else {
// not rolling the dice
// var textColor = '#000000';
// var bgColor = '#D9FAD7';
cb.drawPanel();
}
});

cb.onDrawPanel(function (user) {
return {
'template': '3_rows_12_22_31',
'row1_label': 'Last prize won:',
'row1_value': lastPrizeWon,
'row2_label': 'Last player:',
'row2_value': lastRoller,
'row3_value': stats.tipCounter + ' ' + ((stats.tipCounter > 1)?'tokens':'token') + ' received / rolled ' + stats.rollCounter + ' time(s)'
};
});

cb.onEnter(function (user) {
adBlurb(cb.settings.entry_blurb_size, user.user);
});

cb.onMessage(function(msg) {
var message = msg.m.split(" ");

switch (message[0]) {
case cmdPrefix+'dicehelp':
msg["X-Spam"] = true; // Don't print command messages to chat.
msg.background = "linear-gradient(to right, #CCCCCC, #FFFFFF)";
if(message[1] == 'all' && isPrivileged(msg))adBlurb('Full Blurb');
else adBlurb('Full Blurb', msg.user);
break;
case cmdPrefix+'winners':
msg["X-Spam"] = true;
msg.background = "linear-gradient(to right, #CCCCCC, #FFFFFF)";
if(message[1] == 'all' && isPrivileged(msg))winnersBlurb();
else winnersBlurb(msg.user);
break;
case cmdPrefix+'prizes':
msg["X-Spam"] = true;
msg.background = "linear-gradient(to right, #CCCCCC, #FFFFFF)";
if(message[1] == 'all' && isPrivileged(msg))prizesBlurb();
else prizesBlurb(msg.user);
break;
case cmdPrefix+'dicestats':
msg["X-Spam"] = true;
msg.background = "linear-gradient(to right, #CCCCCC, #FFFFFF)";
if(message[1] == 'all' && isPrivileged(msg))statsBlurb();
else statsBlurb(msg.user);
break;
case cmdPrefix+'diceroll':
if(msg.user == cb.room_slug || (allow_mod_rolls && isPrivileged(msg))) {
msg["X-Spam"] = true;
msg.background = "linear-gradient(to right, #CCCCCC, #FFFFFF)";

var times_to_roll = 1;
if(message.length > 1) {
times_to_roll = Math.min(parseInt(message[1]), maxRollsPerTip);
}

while(times_to_roll > 0) {
roll(msg.user, false);
--times_to_roll;
}
} else if(msg.is_mod === true && !allow_mod_rolls) {
msg["X-Spam"] = true;
msg.background = "linear-gradient(to right, #FF6666, #FFFFFF)";
cb.sendNotice('This model has not allowed mods to use /diceroll.\nThe setting "Allow mods to use the /diceroll command?" needs to be set to "Yes".', msg.user, 'linear-gradient(to right, #FF6666, #FFFFFF)', '#000000', 'bold');
}
break;
default:
break;
}

return msg;
});

function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}

function getRandomFloatInclusive(min, max) {
return Math.random() * (max - min + 1) + min;
}

function isPrivileged(msg) {
return (msg.is_mod === true) || (msg.user == cb.room_slug);
}

function roll(username, count_towards_rare, send_roll_notice_to) {
var prize = 'A Thank You!';

if(count_towards_rare)stats.rollCounter += 1;

var dieResults = [];
var total = -numberOfDie;
for(var i = 0; i < numberOfDie; i++) {
dieResults[i] = getRandomIntInclusive(1, die[i].sides);
total += dieResults[i];
}

if(usingRares && (stats.rollCounter > minRollsForRare) && (getRandomFloatInclusive(0,100) <= rareChance)) {
//RARE PRIZE WON!!!
if(numberOfDie == 1) {
dieResults[0] = die[0].sides+1;
total = dieResults[0] - numberOfDie;
} else {
if(getRandomIntInclusive(0,1) === 0) {
dieResults[0] = die[0].sides+1;
dieResults[1] = die[1].sides;
total = dieResults[0] + dieResults[1] - numberOfDie;
} else {
dieResults[0] = die[0].sides;
dieResults[1] = die[1].sides+1;
total = dieResults[0] + dieResults[1] - numberOfDie;
}
}
}

var winner = false;

if (total >= 0 && total < prizeCount) {
if((total+numberOfDie) in stats.rollResultCounts)stats.rollResultCounts[(total+numberOfDie)] += 1;
else stats.rollResultCounts[(total+numberOfDie)] = 1;
winner = true;
prize = prizes[total];
} else {
winner = false;
}

var msg = ((numberOfDie == 1)?diePrefix + dieResults[0]: diePrefix + dieResults[0] + " " + diePrefix + dieResults[1]) + "\n";
msg += username + " rolled " + (total+numberOfDie) + "! \n".toUpperCase();
msg += "Roll #" + stats.rollCounter + " | Prize: " + prize;

var textColor = '#000000';
var bgColor = cb.settings.prize_bg_color; // '#D9FAD7';

if (winner) textColor = cb.settings.prize_fg_color; // '#067D00';
if (total == prizeCount-1) {
bgColor = cb.settings.rare_bg_color; // '#FFDBF3';
textColor = cb.settings.rare_fg_color; // '#A805A6';
}

cb.sendNotice(msg, send_roll_notice_to, bgColor, textColor, 'bold');
lastPrizeWon = prize;
stats.winners.push("Roll #" + stats.rollCounter + " (" + (total+numberOfDie) + "): " + username + " - " + prize);
if(stats.winners.length > 20)stats.winners = stats.winners.slice(-21);
cb.drawPanel();
}

function initPrizes() {
for (var i = 1; i <= maxPrizes; i++) {
if(!('prize_' + i in cb.settings) || cb.settings['prize_' + i] === '' || cb.settings['prize_' + i] == '.' || cb.settings['prize_' + i] == ' ')continue;
if(i%2 == 1)prizes.push(cb.settings['prize_' + i]);
else prizes.unshift(cb.settings['prize_' + i]);
}

prizeCount = prizes.length;
if(usingRares) {
if(prizeCount%2 === 0)prizes.push(prizes.shift());
prizes[prizes.length-1] += " (RARE)";
}

for(var j = 0; j < prizeCount; j++) {
stats.rollResultCounts[(j+numberOfDie)] = 0;
}

die[0].sides = Math.floor(((numberOfDie == 1)? ((cb.settings.last_prize_rare == 'Yes')?prizeCount-1:prizeCount) : ((cb.settings.last_prize_rare == 'Yes')?prizeCount:prizeCount+1)/2));
die[1].sides = Math.ceil(((numberOfDie == 1)? ((cb.settings.last_prize_rare == 'Yes')?prizeCount-1:prizeCount) : ((cb.settings.last_prize_rare == 'Yes')?prizeCount:prizeCount+1)/2));
cb.log(die[0].sides + " " + die[1].sides);

// basically this is +2 because only using +1 for unequal dice odds consistently made the rare prize feel too close in likelyhood by default to just one of the least likely prizes
rareChance *= equalOdds?100/(prizeCount*1.35):100/((die[0].sides+2)*die[1].sides);
cb.log(rareChance);
}

function statsBlurb(username) {
var msg = "##### Dice Roll Statistics #####\n";
for(var rollval in stats.rollResultCounts) {
msg += "Roll " + rollval + ": " + stats.rollResultCounts[rollval] + "\n";
}

msg += "Total Rolls: " + stats.rollCounter;

cb.sendNotice(msg, username, cb.settings.info_bg_color, cb.settings.info_fg_color, 'bold'); // '#DBFBFF', '#008596'
}

function prizesBlurb(username) {
var msg = "No Prizes!! D:";
if (prizes.length) {
msg = "##### POSSIBLE PRIZES #####";
for (var i = 0; i < prizeCount; i++) {
msg += "\nRoll " + (i+numberOfDie) + " - " + prizes[i];
}
}
cb.sendNotice(msg, username, cb.settings.info_bg_color, cb.settings.info_fg_color, 'bold'); // '#DBFBFF', '#008596'
}

function winnersBlurb(username) {
var msg = "##### LAST 20 WINNERS #####";
msg += "\nList sorted in chronological order";
if (stats.winners.length === 0) {
cb.sendNotice('No one has won anything yet. Roll the dice to win a prize!', username, '', '', 'bold');
} else {
var recentWinners = stats.winners.slice(-21);
for (var i = 0; i < recentWinners.length; i++) msg += "\n" + recentWinners[i];

cb.sendNotice(msg, username, cb.settings.winners_bg_color, cb.settings.winners_fg_color, 'bold'); // '#FFF0DE', '#8A4900'
}
}

function adBlurb(blurb_size, username) {
var msg = "";

switch(blurb_size) {
case 'Off':
break;
case 'Without "all" or Bot Name':
msg += "Tip " + rollprice + " " + ((rollprice>1)?'tokens':'token') + " to roll the dice. ";
msg += "There are " + prizes.length + " possible prizes.\n";
if(maxRollsPerTip>1) {
msg += "You can roll up to " + maxRollsPerTip + " times with a single tip (" + (maxRollsPerTip*rollprice) + " tokens).\n";
if(perfectMults && rollprice>1) msg += "You must tip exact amounts to roll the dice.\n(1 roll for " + rollprice + " tokens, 2 for " + rollprice*2 + " tokens, but " + Math.ceil(rollprice*1.5) + " tokens won't roll the dice).\n";
}
msg += "Type "+cmdPrefix+"prizes to see the list of prizes. \n";
msg += "Type "+cmdPrefix+"winners to see a list of the last 20 winners.";
break;

case 'Short Blurb':
msg += "Tip " + rollprice + " " + ((rollprice>1)?'tokens':'token') + " to roll the dice. ";
msg += "There are " + prizes.length + " possible prizes.\n";
if(maxRollsPerTip>1) {
msg += "You can roll up to " + maxRollsPerTip + " times with a single tip (" + (maxRollsPerTip*rollprice) + " tokens).\n";
if(perfectMults && rollprice>1) msg += "You must tip exact amounts to roll the dice.\n";
}
msg += "Type "+cmdPrefix+"prizes to see the list of prizes. \n";
msg += "Type "+cmdPrefix+"winners to see a list of the last 20 winners.";
break;

case 'Minimal Blurb':
msg += "Tip " + rollprice + " " + ((rollprice>1)?'tokens':'token') + " to roll the dice. ";
break;

case 'Full Blurb':
default:
msg += "A Bettter Dice Roller by aety.\n";
msg += (username)?"(Loosely based on work by: kingchris_, zingknaat, milo_bloom, tablesalt90.)\n":"";
msg += "Tip " + rollprice + " " + ((rollprice>1)?'tokens':'token') + " to roll the dice. ";
msg += "There are " + prizes.length + " possible prizes.\n";
if(maxRollsPerTip>1) {
msg += "You can roll up to " + maxRollsPerTip + " times with a single tip (" + (maxRollsPerTip*rollprice) + " tokens).\n";
if(perfectMults && rollprice>1) msg += "You must tip exact amounts to roll the dice.\n(1 roll for " + rollprice + " tokens, 2 for " + rollprice*2 + " tokens, but " + Math.ceil(rollprice*1.5) + " tokens won't roll the dice).\n";
}
msg += "Type "+cmdPrefix+"prizes to see the list of prizes. \n";
msg += "Type "+cmdPrefix+"prizes all to send the list to all viewers if you're a mod or the broadcaster.\n";
msg += "Type "+cmdPrefix+"winners to see a list of the last 20 winners.";
break;
}

if (msg !== "") {
cb.sendNotice(msg, username, notice_bg, notice_fg, 'bold');
}
}

function advertise() {
adBlurb(cb.settings.blurb_size);
if(cb.settings.notice_wait_time > 0)cb.setTimeout(advertise, parseInt(cb.settings.notice_wait_time) * 60 * 1000);
}

function init() {
initPrizes();
advertise();
if (changeSubject) {
cb.changeRoomSubject('Tip ' + rollprice + ' ' + ((rollprice>1)?'tokens':'token') + ' to roll the dice and win a prize!');
}
}

init();

© Copyright Chaturbate 2011- 2026. All Rights Reserved.