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