From 32ce20bd7212207f0e2b497f5f76cc84cd614354 Mon Sep 17 00:00:00 2001 From: DeSqBlocki Date: Tue, 18 Aug 2026 21:03:07 +0200 Subject: [PATCH] added roulette --- buttons/crumbSaucer/roulette.js | 17 + buttons/crumbSaucer/rouletteBet10.js | 18 + buttons/crumbSaucer/rouletteBet100.js | 18 + buttons/crumbSaucer/rouletteBet1000.js | 18 + buttons/crumbSaucer/rouletteBet50.js | 18 + buttons/crumbSaucer/rouletteBet500.js | 18 + buttons/crumbSaucer/rouletteBlack.js | 18 + buttons/crumbSaucer/rouletteEven.js | 18 + buttons/crumbSaucer/rouletteOdd.js | 18 + buttons/crumbSaucer/rouletteRed.js | 18 + buttons/crumbSaucer/rouletteZero.js | 18 + utils/crumbRoulette.js | 1128 ++++++++++++++++++++++++ utils/crumbSaucer.js | 98 +- 13 files changed, 1401 insertions(+), 22 deletions(-) create mode 100644 buttons/crumbSaucer/roulette.js create mode 100644 buttons/crumbSaucer/rouletteBet10.js create mode 100644 buttons/crumbSaucer/rouletteBet100.js create mode 100644 buttons/crumbSaucer/rouletteBet1000.js create mode 100644 buttons/crumbSaucer/rouletteBet50.js create mode 100644 buttons/crumbSaucer/rouletteBet500.js create mode 100644 buttons/crumbSaucer/rouletteBlack.js create mode 100644 buttons/crumbSaucer/rouletteEven.js create mode 100644 buttons/crumbSaucer/rouletteOdd.js create mode 100644 buttons/crumbSaucer/rouletteRed.js create mode 100644 buttons/crumbSaucer/rouletteZero.js create mode 100644 utils/crumbRoulette.js diff --git a/buttons/crumbSaucer/roulette.js b/buttons/crumbSaucer/roulette.js new file mode 100644 index 0000000..84f4bfd --- /dev/null +++ b/buttons/crumbSaucer/roulette.js @@ -0,0 +1,17 @@ +const { + showRoulette +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbSaucerRoulette", + + async execute(interaction) { + return showRoulette( + interaction + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteBet10.js b/buttons/crumbSaucer/rouletteBet10.js new file mode 100644 index 0000000..94fd858 --- /dev/null +++ b/buttons/crumbSaucer/rouletteBet10.js @@ -0,0 +1,18 @@ +const { + chooseRouletteBet +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteBet10", + + async execute(interaction) { + return chooseRouletteBet( + interaction, + 10 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteBet100.js b/buttons/crumbSaucer/rouletteBet100.js new file mode 100644 index 0000000..5192620 --- /dev/null +++ b/buttons/crumbSaucer/rouletteBet100.js @@ -0,0 +1,18 @@ +const { + chooseRouletteBet +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteBet100", + + async execute(interaction) { + return chooseRouletteBet( + interaction, + 100 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteBet1000.js b/buttons/crumbSaucer/rouletteBet1000.js new file mode 100644 index 0000000..c841119 --- /dev/null +++ b/buttons/crumbSaucer/rouletteBet1000.js @@ -0,0 +1,18 @@ +const { + chooseRouletteBet +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteBet1000", + + async execute(interaction) { + return chooseRouletteBet( + interaction, + 1000 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteBet50.js b/buttons/crumbSaucer/rouletteBet50.js new file mode 100644 index 0000000..ffbcea0 --- /dev/null +++ b/buttons/crumbSaucer/rouletteBet50.js @@ -0,0 +1,18 @@ +const { + chooseRouletteBet +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteBet50", + + async execute(interaction) { + return chooseRouletteBet( + interaction, + 50 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteBet500.js b/buttons/crumbSaucer/rouletteBet500.js new file mode 100644 index 0000000..e1f1a2c --- /dev/null +++ b/buttons/crumbSaucer/rouletteBet500.js @@ -0,0 +1,18 @@ +const { + chooseRouletteBet +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteBet500", + + async execute(interaction) { + return chooseRouletteBet( + interaction, + 500 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteBlack.js b/buttons/crumbSaucer/rouletteBlack.js new file mode 100644 index 0000000..0bc83df --- /dev/null +++ b/buttons/crumbSaucer/rouletteBlack.js @@ -0,0 +1,18 @@ +const { + playRoulette +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteBlack", + + async execute(interaction) { + return playRoulette( + interaction, + "Black" + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteEven.js b/buttons/crumbSaucer/rouletteEven.js new file mode 100644 index 0000000..2cae990 --- /dev/null +++ b/buttons/crumbSaucer/rouletteEven.js @@ -0,0 +1,18 @@ +const { + playRoulette +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteEven", + + async execute(interaction) { + return playRoulette( + interaction, + "Even" + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteOdd.js b/buttons/crumbSaucer/rouletteOdd.js new file mode 100644 index 0000000..e492865 --- /dev/null +++ b/buttons/crumbSaucer/rouletteOdd.js @@ -0,0 +1,18 @@ +const { + playRoulette +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteOdd", + + async execute(interaction) { + return playRoulette( + interaction, + "Odd" + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteRed.js b/buttons/crumbSaucer/rouletteRed.js new file mode 100644 index 0000000..a9f8cd5 --- /dev/null +++ b/buttons/crumbSaucer/rouletteRed.js @@ -0,0 +1,18 @@ +const { + playRoulette +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteRed", + + async execute(interaction) { + return playRoulette( + interaction, + "Red" + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/rouletteZero.js b/buttons/crumbSaucer/rouletteZero.js new file mode 100644 index 0000000..81e255c --- /dev/null +++ b/buttons/crumbSaucer/rouletteZero.js @@ -0,0 +1,18 @@ +const { + playRoulette +} = require( + "../../utils/crumbRoulette" +); + + +module.exports = { + name: + "crumbRouletteZero", + + async execute(interaction) { + return playRoulette( + interaction, + "Zero" + ); + } +}; \ No newline at end of file diff --git a/utils/crumbRoulette.js b/utils/crumbRoulette.js new file mode 100644 index 0000000..aaec667 --- /dev/null +++ b/utils/crumbRoulette.js @@ -0,0 +1,1128 @@ +const { + EmbedBuilder, + ActionRowBuilder, + ButtonBuilder, + ButtonStyle +} = require("discord.js"); + +const { + getCookieBalance, + transferCookies, + temporaryEphemeralReply +} = require("./cookieEconomy"); + + +/* + * ============================================================ + * CONFIG + * ============================================================ + */ + +const ROULETTE_BETS = [ + 10, + 50, + 100, + 500, + 1000 +]; + + +const ROULETTE_CHOICES = { + Red: { + name: + "Red", + + emoji: + "🔴", + + multiplier: + 2 + }, + + + Black: { + name: + "Black", + + emoji: + "âšĢ", + + multiplier: + 2 + }, + + + Odd: { + name: + "Odd", + + emoji: + "đŸ”ĸ", + + multiplier: + 2 + }, + + + Even: { + name: + "Even", + + emoji: + "2ī¸âƒŖ", + + multiplier: + 2 + }, + + + Zero: { + name: + "Zero", + + emoji: + "đŸŸĸ", + + multiplier: + 36 + } +}; + + +/* + * European Roulette red numbers. + */ + +const RED_NUMBERS = + new Set([ + 1, + 3, + 5, + 7, + 9, + 12, + 14, + 16, + 18, + 19, + 21, + 23, + 25, + 27, + 30, + 32, + 34, + 36 + ]); + + +/* + * ============================================================ + * SHOW ROULETTE + * ============================================================ + */ + +async function showRoulette( + interaction +) { + const ownerId = + getRouletteSessionOwner( + interaction + ); + + + if ( + ownerId !== + interaction.user.id + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This isn't your Crumb Saucer session." + } + ); + } + + + const balance = + await getCookieBalance( + interaction.user.id + ); + + + const embed = + new EmbedBuilder() + .setTitle( + "🎡 Crumb Saucer Roulette" + ) + .setDescription( + [ + "European Roulette — **0 to 36**.", + "", + "Choose your wager.", + "", + "🔴 **Red** — ×2", + "âšĢ **Black** — ×2", + "đŸ”ĸ **Odd** — ×2", + "2ī¸âƒŖ **Even** — ×2", + "đŸŸĸ **Zero** — ×36" + ].join("\n") + ) + .addFields({ + name: + "Your Vault", + + value: + `**${balance.toLocaleString()} đŸĒ**` + }) + .setFooter({ + text: + `crumbRouletteMenu:${interaction.user.id}` + }); + + + return interaction.update({ + embeds: [ + embed + ], + + components: + buildBetButtons() + }); +} + + +/* + * ============================================================ + * CHOOSE WAGER + * ============================================================ + */ + +async function chooseRouletteBet( + interaction, + bet +) { + if ( + !ROULETTE_BETS.includes( + bet + ) + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "That Roulette wager isn't available." + } + ); + } + + + const ownerId = + getRouletteSessionOwner( + interaction + ); + + + if ( + ownerId !== + interaction.user.id + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This isn't your Roulette table." + } + ); + } + + + const balance = + await getCookieBalance( + interaction.user.id + ); + + + if ( + balance < + bet + ) { + return temporaryEphemeralReply( + interaction, + { + content: + `You need **${bet.toLocaleString()} Cookies** for that wager.` + } + ); + } + + + const embed = + new EmbedBuilder() + .setTitle( + "🎡 Crumb Saucer Roulette" + ) + .setDescription( + [ + `Wager: **${bet.toLocaleString()} đŸĒ**`, + "", + "Where do you want to place your Cookies?", + "", + "🔴 **Red** — ×2", + "âšĢ **Black** — ×2", + "đŸ”ĸ **Odd** — ×2", + "2ī¸âƒŖ **Even** — ×2", + "đŸŸĸ **Zero** — ×36" + ].join("\n") + ) + .setFooter({ + text: + `crumbRouletteBet:${interaction.user.id}:${bet}` + }); + + + return interaction.update({ + embeds: [ + embed + ], + + components: + buildChoiceButtons() + }); +} + + +/* + * ============================================================ + * PLAY ROULETTE + * ============================================================ + */ + +async function playRoulette( + interaction, + choiceKey +) { + const choice = + ROULETTE_CHOICES[ + choiceKey + ]; + + + if (!choice) { + return temporaryEphemeralReply( + interaction, + { + content: + "That Roulette bet doesn't exist." + } + ); + } + + + const footer = + interaction.message + .embeds[0] + ?.footer + ?.text; + + + const parts = + footer?.split(":") + ?? + []; + + + if ( + parts[0] !== + "crumbRouletteBet" + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This Roulette wager is no longer valid." + } + ); + } + + + const ownerId = + parts[1]; + + + const bet = + Number( + parts[2] + ); + + + if ( + ownerId !== + interaction.user.id + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This isn't your Roulette table." + } + ); + } + + + if ( + !ROULETTE_BETS.includes( + bet + ) + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "That Roulette wager is invalid." + } + ); + } + + + const playerId = + interaction.user.id; + + + const houseId = + interaction.client.user.id; + + + const [ + playerBalance, + houseBalance + ] = + await Promise.all([ + getCookieBalance( + playerId + ), + + getCookieBalance( + houseId + ) + ]); + + + if ( + playerBalance < + bet + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "You no longer have enough Cookies for that wager." + } + ); + } + + + /* + * Worst payout is Zero at x36. + * + * Once the player's wager is transferred to the house, + * the house must still be able to cover the remaining + * potential payout. + */ + + const maximumPayout = + bet * + choice.multiplier; + + + if ( + houseBalance + + bet < + maximumPayout + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "The Crumb Saucer vault can't currently cover that Roulette wager." + } + ); + } + + + /* + * Take wager. + */ + + try { + await transferCookies( + playerId, + houseId, + bet + ); + + } catch (error) { + if ( + error.message === + "INSUFFICIENT_COOKIES" + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "Your balance changed before the wheel could spin." + } + ); + } + + + throw error; + } + + + /* + * ======================================================== + * PSEUDO ANIMATION + * ======================================================== + */ + + await interaction.update({ + embeds: [ + buildSpinEmbed( + choice, + bet, + randomRouletteNumber(), + "The wheel begins to spin..." + ) + ], + + components: [] + }); + + + await sleep( + 650 + ); + + + await interaction.editReply({ + embeds: [ + buildSpinEmbed( + choice, + bet, + randomRouletteNumber(), + "Round and round..." + ) + ], + + components: [] + }); + + + await sleep( + 650 + ); + + + await interaction.editReply({ + embeds: [ + buildSpinEmbed( + choice, + bet, + randomRouletteNumber(), + "The ball is slowing down..." + ) + ], + + components: [] + }); + + + await sleep( + 750 + ); + + + /* + * The real result is only generated after + * the animation completes. + */ + + const result = + randomRouletteNumber(); + + + const won = + didRouletteBetWin( + choiceKey, + result + ); + + + const payout = + won + ? bet * + choice.multiplier + : 0; + + + if ( + payout > + 0 + ) { + try { + await transferCookies( + houseId, + playerId, + payout + ); + + } catch (error) { + console.error( + "[Roulette] Payout failed:", + error + ); + + + return interaction.editReply({ + embeds: [ + new EmbedBuilder() + .setTitle( + "🎡 Roulette Error" + ) + .setDescription( + "The wheel resolved, but the payout failed. Please contact an administrator." + ) + ], + + components: + buildResultButtons() + }); + } + } + + + const [ + playerAfter, + houseAfter + ] = + await Promise.all([ + getCookieBalance( + playerId + ), + + getCookieBalance( + houseId + ) + ]); + + + const profit = + payout - + bet; + + + const resultEmbed = + new EmbedBuilder() + .setTitle( + won + ? "🎉 Roulette Winner!" + : "🎡 Roulette" + ) + .setDescription( + [ + "## The ball lands on...", + "", + `# ${formatRouletteNumber(result)}`, + "", + `${choice.emoji} Your bet: **${choice.name}**`, + "", + won + ? `🎉 **YOU WIN ${payout.toLocaleString()} đŸĒ!**` + : "The house takes this one." + ].join("\n") + ) + .addFields( + { + name: + "Wager", + + value: + `${bet.toLocaleString()} đŸĒ`, + + inline: + true + }, + + { + name: + "Payout", + + value: + `${payout.toLocaleString()} đŸĒ`, + + inline: + true + }, + + { + name: + "Profit", + + value: + `${profit >= 0 ? "+" : ""}${profit.toLocaleString()} đŸĒ`, + + inline: + true + }, + + { + name: + "Your Vault", + + value: + `${playerAfter.toLocaleString()} đŸĒ`, + + inline: + true + }, + + { + name: + "House Vault", + + value: + `${houseAfter.toLocaleString()} đŸĒ`, + + inline: + true + } + ) + .setFooter({ + text: + `crumbRouletteResult:${interaction.user.id}` + }); + + + return interaction.editReply({ + embeds: [ + resultEmbed + ], + + components: + buildResultButtons() + }); +} + + +/* + * ============================================================ + * RESULT LOGIC + * ============================================================ + */ + +function didRouletteBetWin( + choice, + number +) { + switch (choice) { + case "Red": + return ( + number !== + 0 + && + RED_NUMBERS.has( + number + ) + ); + + + case "Black": + return ( + number !== + 0 + && + !RED_NUMBERS.has( + number + ) + ); + + + case "Odd": + return ( + number !== + 0 + && + number % + 2 === + 1 + ); + + + case "Even": + return ( + number !== + 0 + && + number % + 2 === + 0 + ); + + + case "Zero": + return ( + number === + 0 + ); + + + default: + return false; + } +} + + +/* + * ============================================================ + * DISPLAY + * ============================================================ + */ + +function buildSpinEmbed( + choice, + bet, + number, + status +) { + return new EmbedBuilder() + .setTitle( + "🎡 Crumb Saucer Roulette" + ) + .setDescription( + [ + "## 🎡 Spinning...", + "", + `### ${formatRouletteNumber(number)}`, + "", + `**${status}**` + ].join("\n") + ) + .addFields( + { + name: + "Bet", + + value: + `${choice.emoji} ${choice.name}`, + + inline: + true + }, + + { + name: + "Wager", + + value: + `${bet.toLocaleString()} đŸĒ`, + + inline: + true + } + ); +} + + +function formatRouletteNumber( + number +) { + if ( + number === + 0 + ) { + return "đŸŸĸ 0"; + } + + + if ( + RED_NUMBERS.has( + number + ) + ) { + return `🔴 ${number}`; + } + + + return `âšĢ ${number}`; +} + + +/* + * ============================================================ + * BUTTON BUILDERS + * ============================================================ + */ + +function buildBetButtons() { + return [ + new ActionRowBuilder() + .addComponents( + ...ROULETTE_BETS.map( + bet => + new ButtonBuilder() + .setCustomId( + `crumbRouletteBet${bet}` + ) + .setLabel( + bet.toLocaleString() + ) + .setEmoji( + "đŸĒ" + ) + .setStyle( + bet >= + 500 + ? ButtonStyle.Primary + : ButtonStyle.Secondary + ) + ) + ), + + new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId( + "crumbSaucerBack" + ) + .setLabel( + "Back to Crumb Saucer" + ) + .setEmoji( + "â—€ī¸" + ) + .setStyle( + ButtonStyle.Secondary + ) + ) + ]; +} + + +function buildChoiceButtons() { + return [ + new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId( + "crumbRouletteRed" + ) + .setLabel( + "Red" + ) + .setEmoji( + "🔴" + ) + .setStyle( + ButtonStyle.Danger + ), + + new ButtonBuilder() + .setCustomId( + "crumbRouletteBlack" + ) + .setLabel( + "Black" + ) + .setEmoji( + "âšĢ" + ) + .setStyle( + ButtonStyle.Secondary + ), + + new ButtonBuilder() + .setCustomId( + "crumbRouletteOdd" + ) + .setLabel( + "Odd" + ) + .setStyle( + ButtonStyle.Primary + ), + + new ButtonBuilder() + .setCustomId( + "crumbRouletteEven" + ) + .setLabel( + "Even" + ) + .setStyle( + ButtonStyle.Primary + ), + + new ButtonBuilder() + .setCustomId( + "crumbRouletteZero" + ) + .setLabel( + "Zero" + ) + .setEmoji( + "đŸŸĸ" + ) + .setStyle( + ButtonStyle.Success + ) + ), + + new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId( + "crumbSaucerRoulette" + ) + .setLabel( + "Change Wager" + ) + .setEmoji( + "â†Šī¸" + ) + .setStyle( + ButtonStyle.Secondary + ) + ) + ]; +} + + +function buildResultButtons() { + return [ + new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId( + "crumbSaucerRoulette" + ) + .setLabel( + "Play Again" + ) + .setEmoji( + "🎡" + ) + .setStyle( + ButtonStyle.Primary + ), + + new ButtonBuilder() + .setCustomId( + "crumbSaucerBack" + ) + .setLabel( + "Crumb Saucer" + ) + .setEmoji( + "â—€ī¸" + ) + .setStyle( + ButtonStyle.Secondary + ) + ) + ]; +} + + +/* + * ============================================================ + * SESSION OWNER + * ============================================================ + */ + +function getRouletteSessionOwner( + interaction +) { + const footer = + interaction.message + .embeds[0] + ?.footer + ?.text; + + + const parts = + footer?.split(":") + ?? + []; + + + /* + * Main Saucer + */ + if ( + parts[0] === + "crumbSaucer" + ) { + return parts[1]; + } + + + /* + * Roulette wager menu + */ + if ( + parts[0] === + "crumbRouletteMenu" + ) { + return parts[1]; + } + + + /* + * Roulette result + */ + if ( + parts[0] === + "crumbRouletteResult" + ) { + return parts[1]; + } + + + return null; +} + + +/* + * ============================================================ + * RANDOM + * ============================================================ + */ + +function randomRouletteNumber() { + return Math.floor( + Math.random() * + 37 + ); +} + + +function sleep( + ms +) { + return new Promise( + resolve => + setTimeout( + resolve, + ms + ) + ); +} + + +module.exports = { + showRoulette, + chooseRouletteBet, + playRoulette +}; \ No newline at end of file diff --git a/utils/crumbSaucer.js b/utils/crumbSaucer.js index 41a603c..f4e2e3d 100644 --- a/utils/crumbSaucer.js +++ b/utils/crumbSaucer.js @@ -145,18 +145,56 @@ async function showCrumbSaucer( }); } +function getSlotsSessionOwner( + interaction +) { + const footer = + interaction.message + .embeds[0] + ?.footer + ?.text; + + const parts = + footer?.split(":") + ?? + []; + + + /* + * Main Crumb Saucer + */ + if ( + parts[0] === + "crumbSaucer" + ) { + return parts[1]; + } + + + /* + * Slots menu/result + */ + if ( + parts[0] === + "crumbSaucerSlots" + ) { + return parts[1]; + } + + + return null; +} /* * ============================================================ * SLOT MENU * ============================================================ */ - async function showSlots( interaction ) { const ownerId = - getMenuOwner( + getSlotsSessionOwner( interaction ); @@ -169,7 +207,7 @@ async function showSlots( interaction, { content: - "This isn't your Crumb Saucer session. Open your own with `/crumbsaucer`." + "This isn't your Crumb Saucer session." } ); } @@ -222,7 +260,6 @@ async function showSlots( }); } - /* * ============================================================ * PLAY SLOTS @@ -629,7 +666,10 @@ function buildMainMenuEmbed( "### 🃏 Blackjack", "Beat the dealer without going over 21.", "", - "*More attractions coming soon...*" + "### 🎡 Roulette", + "Bet on Red, Black, Odd, Even, or Zero.", + "", + "*More attractions coming soon... make a lala offering or buy me a white monster*" ].join("\n") ) .addFields({ @@ -670,20 +710,6 @@ function buildMainMenuButtons() { ButtonStyle.Primary ), - new ButtonBuilder() - .setCustomId( - "crumbSaucerHeist" - ) - .setLabel( - "Vault Heist" - ) - .setEmoji( - "đŸĻ" - ) - .setStyle( - ButtonStyle.Danger - ), - new ButtonBuilder() .setCustomId( "crumbSaucerBlackjack" @@ -696,6 +722,34 @@ function buildMainMenuButtons() { ) .setStyle( ButtonStyle.Success + ), + + new ButtonBuilder() + .setCustomId( + "crumbSaucerRoulette" + ) + .setLabel( + "Roulette" + ) + .setEmoji( + "🎡" + ) + .setStyle( + ButtonStyle.Primary + ), + + new ButtonBuilder() + .setCustomId( + "crumbSaucerHeist" + ) + .setLabel( + "Vault Heist" + ) + .setEmoji( + "đŸĻ" + ) + .setStyle( + ButtonStyle.Danger ) ) ]; @@ -1023,11 +1077,11 @@ function buildSlotDisplay( grid ) { return [ - "╔═════════════╗", + "╔═════════╗", `║ ${grid[0][0]} │ ${grid[0][1]} │ ${grid[0][2]} ║`, `║ ${grid[1][0]} │ ${grid[1][1]} │ ${grid[1][2]} ║`, `║ ${grid[2][0]} │ ${grid[2][1]} │ ${grid[2][2]} ║`, - "╚═════════════╝" + "╚═════════╝" ].join("\n"); } @@ -1080,7 +1134,7 @@ function getSlotResultName( switch ( - outcome.symbol + outcome.symbol ) { case "🍋": return "🍋 Triple Lemon!";