BlackJack
- community
- minigames
- by Mad
- Takaro v0.0.24
- all
Mad_Blackjack: Casino Gaming on Your Game Server
Add a fully-functional Blackjack card game to your server, letting players gamble their in-game currency through an intuitive command system.
Key Features:
- Complete Blackjack Experience: Play the classic casino card game directly in your game chat with simple commands
- Economy Integration: Connect with Takaro's economy system - bet, win, and lose in-game currency
Game Commands:
- /bjplay - Start a new game with your bet

- /bjstatus - Check your current game status

- /bjhit - Draw another card

- /bjstand - End your turn

Game Features:
- Clear Visual Interface: Card values and suits display with intuitive symbols
- Real-time Information: See your hand total, the dealer's visible card, and your current balance
- In-game Betting: Place wagers directly from your in-game currency balance
- Standard Casino Rules: Beat the dealer without going over 21
- Multiple Card Draws: Take as many cards as you dare with the
/bjhitcommand - Bust Detection: Immediate feedback when you go over 21 and lose your bet
- Automated Dealer: When you stand, the dealer follows standard Blackjack rules
- Transparent Results: See both hands with their totals when the game concludes
- Automatic Currency Management: Winnings are instantly credited to your account
Perfect For:
- Adding entertainment options to your server
- Providing fun ways for players to use in-game currency
- Increasing player engagement with mini-games
- Creating a social casino experience in your game
Requirements:
- Takaro's economy system must be enabled
Configuration 0
Settings you fill in when installing the module on a server.
This module takes no configuration.
Raw config schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [],
"additionalProperties": false
} Raw UI schema
{} Commands 4
Chat commands players trigger in game.
-
BJstatus
No help text available
Command source
import { takaro, data, TakaroUserError } from '@takaro/helpers'; async function main() { const { player } = data; const gameServerId = data.gameServerId; // Return object required by Takaro const result = { success: true, message: 'Status retrieved successfully' }; try { // Card utilities const CardUtils = { // Calculate the total value of a hand, accounting for Aces calculateHandValue(hand) { let total = hand.reduce((sum, card) => sum + card.numericValue, 0); const aces = hand.filter(card => card.value === 'A').length; // Adjust for Aces for (let i = 0; i < aces; i++) { if (total > 21) { total -= 10; } } return total; }, // Convert hand to a readable string with colored cards and resets handToPlayerString(hand) { return hand.map(card => `[8BD3E6]${card.value}${card.suit}[-]`).join(', '); } }; // Game storage functions const GameStorage = { // Variable key for game state getGameKey(playerId) { return `bj_game_${playerId}`; }, // Load game state async loadGame(playerId) { const key = this.getGameKey(playerId); try { const gameVar = await takaro.variable.variableControllerSearch({ filters: { key: [key], playerId: [playerId] } }); if (gameVar.data.data.length > 0) { return JSON.parse(gameVar.data.data[0].value); } return null; } catch (error) { console.error('Failed to load game state:', error); throw new Error('Failed to load game state'); } } }; // Get current game const getActiveGame = async (playerId) => { const game = await GameStorage.loadGame(playerId); if (!game || game.status !== 'active') { throw new Error('You do not have an active game. Start a new game with /bjplay.'); } return game; }; // Execute status check const game = await getActiveGame(player.id); // Prepare hand information const playerHandString = CardUtils.handToPlayerString(game.playerHand); const playerHandValue = CardUtils.calculateHandValue(game.playerHand); const dealerUpCardString = `[FF6D6A]${game.dealerHand[0].value}${game.dealerHand[0].suit}[-]`; // Get player's balance using the economy API const playerInfo = await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, player.id); const playerBalance = playerInfo.data.currency; await player.pm(`[-]BLACKJACK STATUS Bet: ${game.bet} Your Hand: ${playerHandString} (Total: ${playerHandValue}) Dealer's Visible Card: ${dealerUpCardString} Your Balance: ${playerBalance} Use /bjhit to take another card Use /bjstand to end your turn`); return result; } catch (error) { console.error('BJstatus error:', error); result.success = false; result.message = error.message; try { await player.pm(`[-]Error: ${error.message}`); } catch { // Ignore messaging errors } return result; } } // Call the main function with await await main(); -
BJstand
No help text available
Command source
import { takaro, data, TakaroUserError } from '@takaro/helpers'; async function main() { const { player } = data; const gameServerId = data.gameServerId; // Special player ID to pay when a player loses const housePlayerID = "d8ef3f8a-4c8f-4a91-9c44-ab5432f5c6ec"; // Return object required by Takaro const result = { success: true, message: 'Stand successful' }; try { // Card utilities const CardUtils = { // Calculate the total value of a hand, accounting for Aces calculateHandValue(hand) { let total = hand.reduce((sum, card) => sum + card.numericValue, 0); const aces = hand.filter(card => card.value === 'A').length; // Adjust for Aces for (let i = 0; i < aces; i++) { if (total > 21) { total -= 10; } } return total; }, // Convert hand to a readable string with colored cards and resets handToPlayerString(hand) { return hand.map(card => `[8BD3E6]${card.value}${card.suit}[-]`).join(', '); }, // Convert hand to a readable string with colored cards and resets handToDealerString(hand) { return hand.map(card => `[FF6D6A]${card.value}${card.suit}[-]`).join(', '); } }; // Game storage functions const GameStorage = { // Variable key for game state getGameKey(playerId) { return `bj_game_${playerId}`; }, // Save game state async saveGame(game) { const key = this.getGameKey(game.playerId); const gameData = JSON.stringify(game); try { // Search for existing game variable const existingVar = await takaro.variable.variableControllerSearch({ filters: { key: [key], playerId: [game.playerId] } }); if (existingVar.data.data.length > 0) { // Update existing variable await takaro.variable.variableControllerUpdate(existingVar.data.data[0].id, { value: gameData }); } else { // Create new variable await takaro.variable.variableControllerCreate({ key, value: gameData, playerId: game.playerId }); } } catch (error) { console.error('Failed to save game state:', error); throw new Error('Failed to save game state'); } }, // Load game state async loadGame(playerId) { const key = this.getGameKey(playerId); try { const gameVar = await takaro.variable.variableControllerSearch({ filters: { key: [key], playerId: [playerId] } }); if (gameVar.data.data.length > 0) { return JSON.parse(gameVar.data.data[0].value); } return null; } catch (error) { console.error('Failed to load game state:', error); throw new Error('Failed to load game state'); } } }; // Get current game const getActiveGame = async (playerId) => { const game = await GameStorage.loadGame(playerId); if (!game || game.status !== 'active') { throw new Error('You do not have an active game. Start a new game with /bjplay.'); } return game; }; // Stand function - dealer plays and resolves the game const stand = async (playerId) => { // Get active game const game = await getActiveGame(playerId); // No need to create a new deck, use the existing dealer and player hands // Dealer draws cards until 17 or higher let dealerValue = CardUtils.calculateHandValue(game.dealerHand); while (dealerValue < 17) { // If needed, you might want to add a method to draw a card from an existing partial deck // For now, we'll assume the game state contains the full hand game.dealerHand.push({ suit: '[FF6D6A]♠[-]', // Default suit, adjust as needed value: '10', numericValue: 10 }); dealerValue = CardUtils.calculateHandValue(game.dealerHand); } // Determine result const playerValue = CardUtils.calculateHandValue(game.playerHand); if (playerValue > 21) { game.result = 'lose'; // Player busted } else if (dealerValue > 21) { game.result = 'win'; // Dealer busted } else if (playerValue > dealerValue) { game.result = 'win'; // Player has higher value } else if (dealerValue > playerValue) { game.result = 'lose'; // Dealer has higher value } else { game.result = 'push'; // Equal values - push } // Mark game as complete game.status = 'complete'; // Save updated game await GameStorage.saveGame(game); return game; }; // Execute stand const game = await stand(player.id); // Prepare hand information const playerHandString = CardUtils.handToPlayerString(game.playerHand); const playerHandValue = CardUtils.calculateHandValue(game.playerHand); const dealerHandString = CardUtils.handToDealerString(game.dealerHand); const dealerHandValue = CardUtils.calculateHandValue(game.dealerHand); // Process game result let resultMessage = ''; let winnings = 0; // Using hardcoded values instead of config const blackjackPayout = 1.5; switch (game.result) { case 'win': // Check for blackjack bonus if (playerHandValue === 21 && game.playerHand.length === 2) { winnings = game.bet + Math.floor(game.bet * blackjackPayout); resultMessage = `BLACKJACK! You win ${winnings}!`; } else { winnings = game.bet * 2; resultMessage = `You win ${winnings}!`; } // Add winnings using correct currency API await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, player.id, { currency: winnings }); break; case 'push': // Return bet using correct currency API await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, player.id, { currency: game.bet }); resultMessage = "It's a tie (Push). Your bet has been returned."; break; case 'lose': // Transfer bet to house player try { await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, housePlayerID, { currency: game.bet }); console.log(`Transferred ${game.bet} to house player ${housePlayerID} from player ${player.id}`); } catch (transferError) { console.error('Failed to transfer bet to house player:', transferError); } resultMessage = `You lose ${game.bet}.`; break; } // Send game results await player.pm(`[-]GAME OVER`); await player.pm(`Your Hand: ${playerHandString} (Total: ${playerHandValue})}`); await player.pm(`Dealer's Hand: ${dealerHandString} (Total: ${dealerHandValue})}`); await player.pm(`${resultMessage}`); return result; } catch (error) { console.error('BJstand error:', error); result.success = false; result.message = error.message; try { await player.pm(`[-]Error: ${error.message}`); } catch { // Ignore messaging errors } return result; } } // Call the main function with await await main(); -
BJhit
No help text available
Command source
import { takaro, data, TakaroUserError } from '@takaro/helpers'; async function main() { const { player } = data; const gameServerId = data.gameServerId; // Return object required by Takaro const result = { success: true, message: 'Hit successful' }; try { // Card utilities const CardUtils = { // Calculate the total value of a hand, accounting for Aces calculateHandValue(hand) { let total = hand.reduce((sum, card) => sum + card.numericValue, 0); const aces = hand.filter(card => card.value === 'A').length; // Adjust for Aces for (let i = 0; i < aces; i++) { if (total > 21) { total -= 10; } } return total; }, // Convert hand to a readable string with colored cards and resets handToPlayerString(hand) { return hand.map(card => `[8BD3E6]${card.value}${card.suit}[-]`).join(', '); }, // Create a full deck of 52 cards createDeck() { const suits = ['[0C0C0C]♠[-]', '[D64C4C]♥[-]', '[D64C4C]♦[-]', '[0C0C0C]♣[-]']; // Spades, Hearts, Diamonds, Clubs const values = [ { value: '2', numericValue: 2 }, { value: '3', numericValue: 3 }, { value: '4', numericValue: 4 }, { value: '5', numericValue: 5 }, { value: '6', numericValue: 6 }, { value: '7', numericValue: 7 }, { value: '8', numericValue: 8 }, { value: '9', numericValue: 9 }, { value: '10', numericValue: 10 }, { value: 'J', numericValue: 10 }, { value: 'Q', numericValue: 10 }, { value: 'K', numericValue: 10 }, { value: 'A', numericValue: 11 } ]; const deck = []; suits.forEach(suit => { values.forEach(val => { deck.push({ suit, value: val.value, numericValue: val.numericValue }); }); }); return this.shuffleDeck(deck); }, // Shuffle the deck using Fisher-Yates algorithm 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]]; } return deck; } }; // Game storage functions const GameStorage = { // Variable key for game state getGameKey(playerId) { return `bj_game_${playerId}`; }, // Save game state async saveGame(game) { const key = this.getGameKey(game.playerId); const gameData = JSON.stringify(game); try { // Search for existing game variable const existingVar = await takaro.variable.variableControllerSearch({ filters: { key: [key], playerId: [game.playerId] } }); if (existingVar.data.data.length > 0) { // Update existing variable await takaro.variable.variableControllerUpdate(existingVar.data.data[0].id, { value: gameData }); } else { // Create new variable await takaro.variable.variableControllerCreate({ key, value: gameData, playerId: game.playerId }); } } catch (error) { console.error('Failed to save game state:', error); throw new Error('Failed to save game state'); } }, // Load game state async loadGame(playerId) { const key = this.getGameKey(playerId); try { const gameVar = await takaro.variable.variableControllerSearch({ filters: { key: [key], playerId: [playerId] } }); if (gameVar.data.data.length > 0) { return JSON.parse(gameVar.data.data[0].value); } return null; } catch (error) { console.error('Failed to load game state:', error); throw new Error('Failed to load game state'); } } }; // Get current game const getActiveGame = async (playerId) => { const game = await GameStorage.loadGame(playerId); if (!game || game.status !== 'active') { throw new Error('You do not have an active game. Start a new game with /bjplay.'); } return game; }; // Hit function - add a card to player's hand const hit = async (playerId) => { // Get active game const game = await getActiveGame(playerId); // Create a deck (not efficient but works for simplicity) const deck = CardUtils.createDeck(); // Deal a new card to player game.playerHand.push(deck.pop()); // Update last activity game.lastActivity = Date.now(); // Check if player busts const playerValue = CardUtils.calculateHandValue(game.playerHand); if (playerValue > 21) { game.result = 'lose'; game.status = 'complete'; } // Save updated game await GameStorage.saveGame(game); return game; }; // Execute hit const game = await hit(player.id); // Prepare hand information const playerHandString = CardUtils.handToPlayerString(game.playerHand); const playerHandValue = CardUtils.calculateHandValue(game.playerHand); // Check if player busted if (game.status === 'complete' && game.result === 'lose') { // Player loses their bet try { const housePlayerID = "d8ef3f8a-4c8f-4a91-9c44-ab5432f5c6ec"; await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, housePlayerID, { currency: game.bet }); console.log(`Transferred ${game.bet} to house player ${housePlayerID} from player ${player.id}`); } catch (transferError) { console.error('Failed to transfer bet to house player:', transferError); } await player.pm(`[-]You drew a card! Your Hand: ${playerHandString} (Total: ${playerHandValue}) BUST! You lose ${game.bet}.`); } else { await player.pm(`[-]You drew a card! Your Hand: ${playerHandString} (Total: ${playerHandValue}) Use /bjhit to take another card Use /bjstand to end your turn`); } return result; } catch (error) { console.error('BJhit error:', error); result.success = false; result.message = error.message; try { await player.pm(`[-]Error: ${error.message}`); } catch { // Ignore messaging errors } return result; } } // Call the main function with await await main(); -
BJplay
Play Black Jack
Argument Type Default Help betnumber 500 bet Command source
import { takaro, data, TakaroUserError } from '@takaro/helpers'; async function main() { const { player, arguments: args } = data; const gameServerId = data.gameServerId; // Special player ID to pay when a player loses const housePlayerID = "d8ef3f8a-4c8f-4a91-9c44-ab5432f5c6ec"; // Return object required by Takaro const result = { success: true, message: 'Game started successfully' }; try { // Card utilities const CardUtils = { // Create a full deck of 52 cards createDeck() { const suits = ['[0C0C0C]♠[-]', '[D64C4C]♥[-]', '[D64C4C]♦[-]', '[0C0C0C]♣[-]']; // Spades, Hearts, Diamonds, Clubs const values = [ { value: '2', numericValue: 2 }, { value: '3', numericValue: 3 }, { value: '4', numericValue: 4 }, { value: '5', numericValue: 5 }, { value: '6', numericValue: 6 }, { value: '7', numericValue: 7 }, { value: '8', numericValue: 8 }, { value: '9', numericValue: 9 }, { value: '10', numericValue: 10 }, { value: 'J', numericValue: 10 }, { value: 'Q', numericValue: 10 }, { value: 'K', numericValue: 10 }, { value: 'A', numericValue: 11 } ]; const deck = []; suits.forEach(suit => { values.forEach(val => { deck.push({ suit, value: val.value, numericValue: val.numericValue }); }); }); return this.shuffleDeck(deck); }, // Shuffle the deck using Fisher-Yates algorithm 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]]; } return deck; }, // Calculate the total value of a hand, accounting for Aces calculateHandValue(hand) { let total = hand.reduce((sum, card) => sum + card.numericValue, 0); const aces = hand.filter(card => card.value === 'A').length; // Adjust for Aces for (let i = 0; i < aces; i++) { if (total > 21) { total -= 10; } } return total; }, // Convert hand to a readable string with colored cards and resets handToPlayerString(hand) { return hand.map(card => `[8BD3E6]${card.value}${card.suit}[-]`).join(', '); }, // Convert hand to a readable string with colored cards and resets handToDealerString(hand) { return hand.map(card => `[FF6D6A]${card.value}${card.suit}[-]`).join(', '); } }; // Game storage functions const GameStorage = { // Variable key for game state getGameKey(playerId) { return `bj_game_${playerId}`; }, // Save game state async saveGame(game) { const key = this.getGameKey(game.playerId); const gameData = JSON.stringify(game); try { // Search for existing game variable const existingVar = await takaro.variable.variableControllerSearch({ filters: { key: [key], playerId: [game.playerId] } }); if (existingVar.data.data.length > 0) { // Update existing variable await takaro.variable.variableControllerUpdate(existingVar.data.data[0].id, { value: gameData }); } else { // Create new variable await takaro.variable.variableControllerCreate({ key, value: gameData, playerId: game.playerId }); } } catch (error) { // Attempt to refund the player's bet since game state couldn't be saved if (game.bet && game.playerId) { try { // Using gameServerId from parent scope await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, game.playerId, { currency: game.bet }); console.log(`Refunded ${game.bet} to player ${game.playerId} due to game state save error`); } catch (refundError) { console.error('Failed to refund player after game state save error:', refundError); } } console.error('Failed to save game state:', error); throw new Error('Failed to save game state. Your bet has been refunded.'); } }, // Load game state async loadGame(playerId) { const key = this.getGameKey(playerId); try { const gameVar = await takaro.variable.variableControllerSearch({ filters: { key: [key], playerId: [playerId] } }); if (gameVar.data.data.length > 0) { return JSON.parse(gameVar.data.data[0].value); } return null; } catch (error) { console.error('Failed to load game state:', error); throw new Error('Failed to load game state'); } } }; // Game logic async function startGame(playerId, bet) { // Check if player already has a game const existingGame = await GameStorage.loadGame(playerId); if (existingGame && existingGame.status === 'active') { throw new Error('You already have an active game. Use /bjstatus to see your current game.'); } // Create deck and deal initial cards const deck = CardUtils.createDeck(); const playerHand = [deck.pop(), deck.pop()]; const dealerHand = [deck.pop(), deck.pop()]; // Create new game state const game = { playerId, playerHand, dealerHand, bet, status: 'active', lastActivity: Date.now() }; // Check for immediate blackjack const playerValue = CardUtils.calculateHandValue(playerHand); const dealerValue = CardUtils.calculateHandValue(dealerHand); if (playerValue === 21) { // Player has blackjack if (dealerValue === 21) { // Both have blackjack - push game.result = 'push'; game.status = 'complete'; } else { // Player wins with blackjack game.result = 'win'; game.status = 'complete'; } } // Save game state await GameStorage.saveGame(game); return game; } // Validate bet if (!args || !args.bet) { await player.pm('[-]Please provide a bet amount. Usage: /BJplay [amount]'); result.success = false; result.message = 'Missing bet amount'; return result; } const bet = parseInt(args.bet); if (isNaN(bet) || bet <= 0) { await player.pm('[-]Please provide a valid bet amount (a positive number).'); result.success = false; result.message = 'Invalid bet amount'; return result; } // Using hardcoded values instead of config const minBet = 10; const maxBet = 10000; const blackjackPayout = 1.5; if (bet < minBet) { await player.pm(`[-]Minimum bet is ${minBet}.`); result.success = false; result.message = `Bet below minimum (${minBet})`; return result; } if (bet > maxBet) { await player.pm(`[-]Maximum bet is ${maxBet}.`); result.success = false; result.message = `Bet above maximum (${maxBet})`; return result; } // Get current player balance const playerInfo = await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, player.id); const playerBalance = playerInfo.data.currency; // Check balance if (playerBalance < bet) { await player.pm(`[-]You don't have enough currency. Your balance: ${playerBalance}`); result.success = false; result.message = 'Insufficient funds'; return result; } // Deduct bet using the correct currency API await takaro.playerOnGameserver.playerOnGameServerControllerDeductCurrency(gameServerId, player.id, { currency: bet }); // Start the game using variable storage const game = await startGame(player.id, bet); // Construct game status message const playerHandString = CardUtils.handToPlayerString(game.playerHand); const playerHandValue = CardUtils.calculateHandValue(game.playerHand); // For dealer's up card, we use the color with reset const dealerUpCardString = `[FF6D6A]${game.dealerHand[0].value}${game.dealerHand[0].suit}[-]`; // Check for immediate game over if (game.status === 'complete') { const dealerHandString = CardUtils.handToDealerString(game.dealerHand); const dealerHandValue = CardUtils.calculateHandValue(game.dealerHand); let resultMessage = ''; let winnings = 0; switch (game.result) { case 'win': // Check for blackjack bonus if (playerHandValue === 21 && game.playerHand.length === 2) { winnings = bet + Math.floor(bet * blackjackPayout); resultMessage = `BLACKJACK! You win ${winnings}!`; } else { winnings = bet * 2; resultMessage = `You win ${winnings}!`; } // Add winnings using correct currency API await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, player.id, { currency: winnings }); break; case 'push': // Return bet using correct currency API await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, player.id, { currency: bet }); resultMessage = 'It\'s a tie (Push). Your bet has been returned.'; break; case 'lose': resultMessage = `You lose ${bet}.`; // Give the bet to the house player try { await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, housePlayerID, { currency: bet }); console.log(`Transferred ${bet} to house player ${housePlayerID} from player ${player.id}`); } catch (transferError) { console.error('Failed to transfer bet to house player:', transferError); } break; } // Send separate messages for better readability (without newlines) await player.pm(`[-]GAME OVER`); await player.pm(`Your Hand: ${playerHandString} (Total: ${playerHandValue})`); await player.pm(`Dealer's Hand: ${dealerHandString} (Total: ${dealerHandValue})`); await player.pm(`${resultMessage}`); } else { // Send separate messages for better readability (without newlines) await player.pm(`[-]BLACKJACK GAME STARTED!`); await player.pm(`Bet: ${bet}`); await player.pm(`Your Hand: ${playerHandString} (Total: ${playerHandValue})`); await player.pm(`Dealer's Visible Card: ${dealerUpCardString}`); await player.pm(`Use /bjhit to take another card Use /bjstand to end your turn`); } return result; } catch (error) { console.error('BJplay error:', error); result.success = false; result.message = error.message; try { await player.pm(`[-]Error: ${error.message}`); } catch { // Ignore messaging errors } return result; } } // Call the main function with await await main();