diff --git a/buttons/crumbSaucer/blackjack.js b/buttons/crumbSaucer/blackjack.js new file mode 100644 index 0000000..f49532c --- /dev/null +++ b/buttons/crumbSaucer/blackjack.js @@ -0,0 +1,17 @@ +const { + showBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbSaucerBlackjack", + + async execute(interaction) { + return showBlackjack( + interaction + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/blackjackBet10.js b/buttons/crumbSaucer/blackjackBet10.js new file mode 100644 index 0000000..bad8114 --- /dev/null +++ b/buttons/crumbSaucer/blackjackBet10.js @@ -0,0 +1,18 @@ +const { + startBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbBlackjackBet10", + + async execute(interaction) { + return startBlackjack( + interaction, + 10 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/blackjackBet100.js b/buttons/crumbSaucer/blackjackBet100.js new file mode 100644 index 0000000..344af5c --- /dev/null +++ b/buttons/crumbSaucer/blackjackBet100.js @@ -0,0 +1,18 @@ +const { + startBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbBlackjackBet100", + + async execute(interaction) { + return startBlackjack( + interaction, + 100 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/blackjackBet1000.js b/buttons/crumbSaucer/blackjackBet1000.js new file mode 100644 index 0000000..d4b5a5e --- /dev/null +++ b/buttons/crumbSaucer/blackjackBet1000.js @@ -0,0 +1,18 @@ +const { + startBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbBlackjackBet1000", + + async execute(interaction) { + return startBlackjack( + interaction, + 1000 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/blackjackBet50.js b/buttons/crumbSaucer/blackjackBet50.js new file mode 100644 index 0000000..4f46fa9 --- /dev/null +++ b/buttons/crumbSaucer/blackjackBet50.js @@ -0,0 +1,18 @@ +const { + startBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbBlackjackBet50", + + async execute(interaction) { + return startBlackjack( + interaction, + 50 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/blackjackBet500.js b/buttons/crumbSaucer/blackjackBet500.js new file mode 100644 index 0000000..dc154fe --- /dev/null +++ b/buttons/crumbSaucer/blackjackBet500.js @@ -0,0 +1,18 @@ +const { + startBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbBlackjackBet500", + + async execute(interaction) { + return startBlackjack( + interaction, + 500 + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/blackjackDouble.js b/buttons/crumbSaucer/blackjackDouble.js new file mode 100644 index 0000000..7e978b0 --- /dev/null +++ b/buttons/crumbSaucer/blackjackDouble.js @@ -0,0 +1,17 @@ +const { + doubleBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbBlackjackDouble", + + async execute(interaction) { + return doubleBlackjack( + interaction + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/blackjackHit.js b/buttons/crumbSaucer/blackjackHit.js new file mode 100644 index 0000000..0cb58d3 --- /dev/null +++ b/buttons/crumbSaucer/blackjackHit.js @@ -0,0 +1,17 @@ +const { + hitBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbBlackjackHit", + + async execute(interaction) { + return hitBlackjack( + interaction + ); + } +}; \ No newline at end of file diff --git a/buttons/crumbSaucer/blackjackStand.js b/buttons/crumbSaucer/blackjackStand.js new file mode 100644 index 0000000..80c6eb3 --- /dev/null +++ b/buttons/crumbSaucer/blackjackStand.js @@ -0,0 +1,17 @@ +const { + standBlackjack +} = require( + "../../utils/crumbBlackjack" +); + + +module.exports = { + name: + "crumbBlackjackStand", + + async execute(interaction) { + return standBlackjack( + interaction + ); + } +}; \ No newline at end of file diff --git a/utils/crumbBlackjack.js b/utils/crumbBlackjack.js new file mode 100644 index 0000000..42ab305 --- /dev/null +++ b/utils/crumbBlackjack.js @@ -0,0 +1,1780 @@ +const { + EmbedBuilder, + ActionRowBuilder, + ButtonBuilder, + ButtonStyle +} = require("discord.js"); + +const { + randomUUID +} = require("node:crypto"); + +const { + getCollections, + getCookieBalance, + transferCookies, + temporaryEphemeralReply, + getUnixTime +} = require("./cookieEconomy"); + + +const GAME_COLLECTION = + "items_games"; + + +const BLACKJACK_BETS = [ + 10, + 50, + 100, + 500, + 1000 +]; + + +/* + * ============================================================ + * SHOW BLACKJACK MENU + * ============================================================ + */ + +async function showBlackjack( + interaction +) { + const ownerId = + getBlackjackSessionOwner( + 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 active = + await getActiveBlackjack( + interaction.user.id, + interaction.guildId + ); + + + if (active) { + return interaction.update({ + embeds: [ + buildGameEmbed( + active + ) + ], + + components: + buildGameButtons( + active + ) + }); + } + + + const embed = + new EmbedBuilder() + .setTitle( + "🃏 Crumb Saucer Blackjack" + ) + + .setDescription( + [ + "Beat the dealer without going over **21**.", + "", + "**Dealer stands on 17.**", + "**Blackjack pays 3:2.**", + "**Push returns your wager.**", + "", + "Choose your wager below." + ].join("\n") + ) + + .addFields({ + name: + "Your Vault", + + value: + `**${balance.toLocaleString()} 🍪**` + }) + + .setFooter({ + text: + `crumbSaucerBlackjack:${interaction.user.id}` + }); + + + return interaction.update({ + embeds: [ + embed + ], + + components: + buildBetButtons() + }); +} + + +/* + * ============================================================ + * START GAME + * ============================================================ + */ + +async function startBlackjack( + interaction, + bet +) { + if ( + !BLACKJACK_BETS.includes( + bet + ) + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "That wager isn't available." + } + ); + } + + + const owner = + getBlackjackMenuOwner( + interaction + ); + + + if ( + owner !== + interaction.user.id + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This isn't your Blackjack table." + } + ); + } + + + const { + db + } = + getCollections(); + + + const games = + db.collection( + GAME_COLLECTION + ); + + + const existing = + await getActiveBlackjack( + interaction.user.id, + interaction.guildId + ); + + + if (existing) { + return temporaryEphemeralReply( + interaction, + { + content: + "You already have an active Blackjack hand." + } + ); + } + + + const balance = + await getCookieBalance( + interaction.user.id + ); + + + if ( + balance < + bet + ) { + return temporaryEphemeralReply( + interaction, + { + content: + `You need **${bet.toLocaleString()} Cookies** for that wager.` + } + ); + } + + + /* + * The house must be able to cover a natural Blackjack. + * + * Total Blackjack payout is 2.5x stake after + * the player's stake has already moved to the house. + */ + + const houseId = + interaction.client.user.id; + + + const houseBalance = + await getCookieBalance( + houseId + ); + + + if ( + houseBalance < + Math.ceil( + bet * + 1.5 + ) + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "The Crumb Saucer vault can't currently cover that wager." + } + ); + } + + + try { + await transferCookies( + interaction.user.id, + houseId, + bet + ); + + } catch (error) { + if ( + error.message === + "INSUFFICIENT_COOKIES" + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "Your Cookie balance changed before the hand could begin." + } + ); + } + + + throw error; + } + + + const deck = + createDeck(); + + + shuffleDeck( + deck + ); + + + const playerCards = [ + deck.pop(), + deck.pop() + ]; + + + const dealerCards = [ + deck.pop(), + deck.pop() + ]; + + + const game = { + eventType: + "Blackjack", + + gameId: + randomUUID(), + + userId: + interaction.user.id, + + guildId: + interaction.guildId, + + channelId: + interaction.channelId, + + state: + "Playing", + + bet, + + originalBet: + bet, + + doubled: + false, + + deck, + + playerCards, + + dealerCards, + + createdAt: + getUnixTime() + }; + + + await games.insertOne( + game + ); + + + /* + * Natural Blackjack checks. + */ + + const playerBlackjack = + isBlackjack( + playerCards + ); + + + const dealerBlackjack = + isBlackjack( + dealerCards + ); + + + if ( + playerBlackjack || + dealerBlackjack + ) { + return resolveInitialBlackjack( + interaction, + game, + playerBlackjack, + dealerBlackjack + ); + } + + + return interaction.update({ + embeds: [ + buildGameEmbed( + game + ) + ], + + components: + buildGameButtons( + game + ) + }); +} + + +/* + * ============================================================ + * HIT + * ============================================================ + */ + +async function hitBlackjack( + interaction +) { + const game = + await getGameFromInteraction( + interaction + ); + + + if (!game) { + return temporaryEphemeralReply( + interaction, + { + content: + "This Blackjack hand no longer exists." + } + ); + } + + + if ( + game.userId !== + interaction.user.id + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This isn't your Blackjack hand." + } + ); + } + + + if ( + game.state !== + "Playing" + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This hand is already finished." + } + ); + } + + + const deck = + [...game.deck]; + + + const playerCards = + [...game.playerCards]; + + + playerCards.push( + deck.pop() + ); + + + const score = + getHandValue( + playerCards + ); + + + const { + db + } = + getCollections(); + + + const games = + db.collection( + GAME_COLLECTION + ); + + + await games.updateOne( + { + gameId: + game.gameId, + + state: + "Playing" + }, + { + $set: { + deck, + playerCards + } + } + ); + + + if ( + score > + 21 + ) { + return finishBlackjack( + interaction, + { + ...game, + deck, + playerCards + }, + "Bust" + ); + } + + + if ( + score === + 21 + ) { + return standBlackjack( + interaction, + { + ...game, + deck, + playerCards + } + ); + } + + + const updated = { + ...game, + deck, + playerCards + }; + + + return interaction.update({ + embeds: [ + buildGameEmbed( + updated + ) + ], + + components: + buildGameButtons( + updated + ) + }); +} + + +/* + * ============================================================ + * STAND + * ============================================================ + */ + +async function standBlackjack( + interaction, + suppliedGame = null +) { + let game = + suppliedGame; + + + if (!game) { + game = + await getGameFromInteraction( + interaction + ); + } + + + if (!game) { + return temporaryEphemeralReply( + interaction, + { + content: + "This Blackjack hand no longer exists." + } + ); + } + + + if ( + game.userId !== + interaction.user.id + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This isn't your Blackjack hand." + } + ); + } + + + const deck = + [...game.deck]; + + + const dealerCards = + [...game.dealerCards]; + + + while ( + getHandValue( + dealerCards + ) + < + 17 + ) { + dealerCards.push( + deck.pop() + ); + } + + + const updated = { + ...game, + deck, + dealerCards + }; + + + const playerScore = + getHandValue( + updated.playerCards + ); + + + const dealerScore = + getHandValue( + dealerCards + ); + + + let result; + + + if ( + dealerScore > + 21 + ) { + result = + "DealerBust"; + } + + else if ( + playerScore > + dealerScore + ) { + result = + "Win"; + } + + else if ( + playerScore < + dealerScore + ) { + result = + "Loss"; + } + + else { + result = + "Push"; + } + + + return finishBlackjack( + interaction, + updated, + result + ); +} + + +/* + * ============================================================ + * DOUBLE + * ============================================================ + */ + +async function doubleBlackjack( + interaction +) { + const game = + await getGameFromInteraction( + interaction + ); + + + if (!game) { + return temporaryEphemeralReply( + interaction, + { + content: + "This Blackjack hand no longer exists." + } + ); + } + + + if ( + game.userId !== + interaction.user.id + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "This isn't your Blackjack hand." + } + ); + } + + + /* + * Double is only allowed on the initial 2-card hand. + */ + + if ( + game.playerCards.length !== + 2 + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "You can only Double on your initial two cards." + } + ); + } + + + const balance = + await getCookieBalance( + interaction.user.id + ); + + + if ( + balance < + game.originalBet + ) { + return temporaryEphemeralReply( + interaction, + { + content: + `You need another **${game.originalBet.toLocaleString()} Cookies** to Double.` + } + ); + } + + + try { + await transferCookies( + interaction.user.id, + interaction.client.user.id, + game.originalBet + ); + + } catch (error) { + if ( + error.message === + "INSUFFICIENT_COOKIES" + ) { + return temporaryEphemeralReply( + interaction, + { + content: + "Your balance changed before you could Double." + } + ); + } + + + throw error; + } + + + const deck = + [...game.deck]; + + + const playerCards = + [...game.playerCards]; + + + playerCards.push( + deck.pop() + ); + + + const updated = { + ...game, + + deck, + playerCards, + + bet: + game.bet + + game.originalBet, + + doubled: + true + }; + + + const { + db + } = + getCollections(); + + + await db + .collection( + GAME_COLLECTION + ) + .updateOne( + { + gameId: + game.gameId, + + state: + "Playing" + }, + { + $set: { + deck, + playerCards, + + bet: + updated.bet, + + doubled: + true + } + } + ); + + + if ( + getHandValue( + playerCards + ) + > + 21 + ) { + return finishBlackjack( + interaction, + updated, + "Bust" + ); + } + + + /* + * Double always automatically stands. + */ + + return standBlackjack( + interaction, + updated + ); +} + + +/* + * ============================================================ + * NATURAL BLACKJACK + * ============================================================ + */ + +async function resolveInitialBlackjack( + interaction, + game, + playerBlackjack, + dealerBlackjack +) { + if ( + playerBlackjack && + dealerBlackjack + ) { + return finishBlackjack( + interaction, + game, + "Push" + ); + } + + + if (playerBlackjack) { + return finishBlackjack( + interaction, + game, + "Blackjack" + ); + } + + + return finishBlackjack( + interaction, + game, + "DealerBlackjack" + ); +} + + +/* + * ============================================================ + * FINISH GAME + * ============================================================ + */ + +async function finishBlackjack( + interaction, + game, + result +) { + const houseId = + interaction.client.user.id; + + + let payout = + 0; + + + switch (result) { + /* + * Regular win returns stake + matching winnings. + */ + + case "Win": + case "DealerBust": + payout = + game.bet * + 2; + break; + + + /* + * Push simply returns the player's stake. + */ + + case "Push": + payout = + game.bet; + break; + + + /* + * Natural Blackjack: + * + * original stake + 3:2 winnings + * + * = 2.5x total returned + */ + + case "Blackjack": + payout = + Math.floor( + game.bet * + 2.5 + ); + break; + + + /* + * Loss / Bust / Dealer Blackjack: + * house keeps the stake. + */ + + default: + payout = + 0; + } + + + if ( + payout > + 0 + ) { + const houseBalance = + await getCookieBalance( + houseId + ); + + + if ( + houseBalance < + payout + ) { + console.error( + `[Blackjack] House cannot cover payout ${payout} for ${game.userId}` + ); + + + return interaction.update({ + embeds: [ + new EmbedBuilder() + .setTitle( + "🃏 Blackjack Error" + ) + .setDescription( + "The Crumb Saucer vault couldn't cover the payout. Please contact an administrator." + ) + ], + + components: + buildReturnButtons() + }); + } + + + await transferCookies( + houseId, + game.userId, + payout + ); + } + + + const { + db + } = + getCollections(); + + + await db + .collection( + GAME_COLLECTION + ) + .updateOne( + { + gameId: + game.gameId, + + state: + "Playing" + }, + { + $set: { + state: + "Finished", + + result, + + payout, + + finishedAt: + getUnixTime(), + + dealerCards: + game.dealerCards, + + playerCards: + game.playerCards + } + } + ); + + + const playerBalance = + await getCookieBalance( + game.userId + ); + + + const profit = + payout - + game.bet; + + +const embed = + new EmbedBuilder() + .setTitle( + getResultTitle( + result + ) + ) + + .setDescription( + [ + "**Dealer**", + formatCards( + game.dealerCards + ), + `Score: **${getHandValue(game.dealerCards)}**`, + "", + "**You**", + formatCards( + game.playerCards + ), + `Score: **${getHandValue(game.playerCards)}**` + ].join("\n") + ) + + .addFields( + { + name: + "Bet", + + value: + `${game.bet.toLocaleString()} 🍪`, + + inline: + true + }, + + { + name: + "Payout", + + value: + `${payout.toLocaleString()} 🍪`, + + inline: + true + }, + + { + name: + "Profit", + + value: + `${profit >= 0 ? "+" : ""}${profit.toLocaleString()} 🍪`, + + inline: + true + }, + + { + name: + "Your Vault", + + value: + `${playerBalance.toLocaleString()} 🍪` + } + ) + + .setFooter({ + text: + `crumbSaucerBlackjack:${game.userId}` + }); + + + return interaction.update({ + embeds: [ + embed + ], + + components: + buildReturnButtons() + }); +} + + +/* + * ============================================================ + * GAME EMBED + * ============================================================ + */ + +function buildGameEmbed( + game +) { + const playerScore = + getHandValue( + game.playerCards + ); + + + const dealerVisible = + game.dealerCards[0]; + + + return new EmbedBuilder() + .setTitle( + "🃏 Crumb Saucer Blackjack" + ) + + .setDescription( + [ + "**Dealer**", + `${formatCard(dealerVisible)} 🂠`, + "", + "**You**", + formatCards( + game.playerCards + ), + `Score: **${playerScore}**` + ].join("\n") + ) + + .addFields( + { + name: + "Bet", + + value: + `**${game.bet.toLocaleString()} 🍪**`, + + inline: + true + }, + + { + name: + "Status", + + value: + "**Your turn**", + + inline: + true + } + ) + + .setFooter({ + text: + `crumbBlackjack:${game.gameId}:${game.userId}` + }); +} + + +/* + * ============================================================ + * BUTTONS + * ============================================================ + */ + +function buildBetButtons() { + return [ + new ActionRowBuilder() + .addComponents( + ...BLACKJACK_BETS.map( + bet => + new ButtonBuilder() + .setCustomId( + `crumbBlackjackBet${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 buildGameButtons( + game +) { + const canDouble = + game.playerCards.length === + 2 + && + !game.doubled; + + + return [ + new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId( + "crumbBlackjackHit" + ) + .setLabel( + "Hit" + ) + .setEmoji( + "➕" + ) + .setStyle( + ButtonStyle.Primary + ), + + new ButtonBuilder() + .setCustomId( + "crumbBlackjackStand" + ) + .setLabel( + "Stand" + ) + .setEmoji( + "✋" + ) + .setStyle( + ButtonStyle.Success + ), + + new ButtonBuilder() + .setCustomId( + "crumbBlackjackDouble" + ) + .setLabel( + "Double" + ) + .setEmoji( + "💰" + ) + .setDisabled( + !canDouble + ) + .setStyle( + ButtonStyle.Danger + ) + ) + ]; +} + + +function buildReturnButtons() { + return [ + new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId( + "crumbSaucerBlackjack" + ) + .setLabel( + "Play Again" + ) + .setEmoji( + "🃏" + ) + .setStyle( + ButtonStyle.Primary + ), + + new ButtonBuilder() + .setCustomId( + "crumbSaucerBack" + ) + .setLabel( + "Crumb Saucer" + ) + .setEmoji( + "◀️" + ) + .setStyle( + ButtonStyle.Secondary + ) + ) + ]; +} + + +/* + * ============================================================ + * CARDS + * ============================================================ + */ + +function createDeck() { + const suits = [ + "♠️", + "♥️", + "♦️", + "♣️" + ]; + + + const ranks = [ + "A", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "J", + "Q", + "K" + ]; + + + const deck = []; + + + for ( + const suit + of suits + ) { + for ( + const rank + of ranks + ) { + deck.push({ + rank, + suit + }); + } + } + + + return deck; +} + + +function shuffleDeck( + deck +) { + for ( + let i = + deck.length - 1; + i > + 0; + i-- + ) { + const j = + Math.floor( + Math.random() * + ( + i + + 1 + ) + ); + + + [ + deck[i], + deck[j] + ] = + [ + deck[j], + deck[i] + ]; + } +} + + +function getHandValue( + cards +) { + let total = + 0; + + let aces = + 0; + + + for ( + const card + of cards + ) { + switch ( + card.rank + ) { + case "A": + total += + 11; + + aces++; + break; + + + case "K": + case "Q": + case "J": + total += + 10; + break; + + + default: + total += + Number( + card.rank + ); + } + } + + + while ( + total > + 21 + && + aces > + 0 + ) { + total -= + 10; + + aces--; + } + + + return total; +} + + +function isBlackjack( + cards +) { + return ( + cards.length === + 2 + && + getHandValue( + cards + ) === + 21 + ); +} + + +function formatCard( + card +) { + return ( + `**${card.rank}${card.suit}**` + ); +} + + +function formatCards( + cards +) { + return cards + .map( + formatCard + ) + .join( + " " + ); +} + + +/* + * ============================================================ + * LOOKUPS + * ============================================================ + */ + +async function getActiveBlackjack( + userId, + guildId +) { + const { + db + } = + getCollections(); + + + return db + .collection( + GAME_COLLECTION + ) + .findOne({ + eventType: + "Blackjack", + + userId, + + guildId, + + state: + "Playing" + }); +} + + +async function getGameFromInteraction( + interaction +) { + const footer = + interaction.message + .embeds[0] + ?.footer + ?.text; + + + const parts = + footer?.split(":") + ?? + []; + + + if ( + parts[0] !== + "crumbBlackjack" + ) { + return null; + } + + + const gameId = + parts[1]; + + + const { + db + } = + getCollections(); + + + return db + .collection( + GAME_COLLECTION + ) + .findOne({ + eventType: + "Blackjack", + + gameId + }); +} + + +/* + * ============================================================ + * MENU OWNERS + * ============================================================ + */ + +function getBlackjackSessionOwner( + interaction +) { + const footer = + interaction.message + .embeds[0] + ?.footer + ?.text; + + + const parts = + footer?.split(":") + ?? + []; + + + /* + * Main Crumb Saucer + * + * crumbSaucer: + */ + if ( + parts[0] === + "crumbSaucer" + ) { + return parts[1]; + } + + + /* + * Blackjack wager / result menu + * + * crumbSaucerBlackjack: + */ + if ( + parts[0] === + "crumbSaucerBlackjack" + ) { + return parts[1]; + } + + + /* + * Active Blackjack hand + * + * crumbBlackjack:: + */ + if ( + parts[0] === + "crumbBlackjack" + ) { + return parts[2]; + } + + + return null; +} + +function getBlackjackMenuOwner( + interaction +) { + const footer = + interaction.message + .embeds[0] + ?.footer + ?.text; + + + const [ + type, + userId + ] = + footer?.split(":") + ?? + []; + + + if ( + type !== + "crumbSaucerBlackjack" + ) { + return null; + } + + + return userId; +} + + +/* + * ============================================================ + * RESULT TITLES + * ============================================================ + */ + +function getResultTitle( + result +) { + switch ( + result + ) { + case "Blackjack": + return "🃏 BLACKJACK!"; + + case "Win": + return "🎉 You Win!"; + + case "DealerBust": + return "💥 Dealer Bust!"; + + case "Push": + return "🤝 Push"; + + case "Bust": + return "💥 Bust!"; + + case "DealerBlackjack": + return "🃏 Dealer Blackjack"; + + default: + return "🃏 Dealer Wins"; + } +} + + +module.exports = { + showBlackjack, + startBlackjack, + hitBlackjack, + standBlackjack, + doubleBlackjack +}; \ No newline at end of file diff --git a/utils/crumbSaucer.js b/utils/crumbSaucer.js index cf64c57..41a603c 100644 --- a/utils/crumbSaucer.js +++ b/utils/crumbSaucer.js @@ -626,6 +626,9 @@ function buildMainMenuEmbed( "### 🏦 Vault Heist", "Attempt to breach ShaiBot's Cookie Vault.", "", + "### 🃏 Blackjack", + "Beat the dealer without going over 21.", + "", "*More attractions coming soon...*" ].join("\n") ) @@ -679,12 +682,25 @@ function buildMainMenuButtons() { ) .setStyle( ButtonStyle.Danger + ), + + new ButtonBuilder() + .setCustomId( + "crumbSaucerBlackjack" + ) + .setLabel( + "Blackjack" + ) + .setEmoji( + "🃏" + ) + .setStyle( + ButtonStyle.Success ) ) ]; } - function buildSlotBetButtons() { return [ new ActionRowBuilder()