{
  "name": "Roulette",
  "author": "limon",
  "supportedGames": [
    "all"
  ],
  "takaroVersion": "v0.0.21",
  "versions": [
    {
      "tag": "0.0.3",
      "description": "A casino-style roulette game where players can bet currency on different outcomes.",
      "configSchema": "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"required\":[],\"additionalProperties\":false,\"properties\":{\"minimumBet\":{\"title\":\"minimumBet\",\"description\":\"The minimum amount a player can bet on a single spin.\",\"default\":10,\"type\":\"number\"},\"maximumBet\":{\"title\":\"maximumBet\",\"description\":\"The maximum amount a player can bet on a single spin.\",\"default\":100,\"type\":\"number\"},\"houseEdge\":{\"title\":\"houseEdge\",\"description\":\"Percentage of winnings the house takes as profit (5 = 5%).\",\"default\":5,\"type\":\"number\"}}}",
      "uiSchema": "{}",
      "commands": [
        {
          "name": "roulettereset",
          "trigger": "roulettereset",
          "helpText": "Reset a player's roulette statistics (admin only).",
          "function": "import { takaro, data, checkPermission, TakaroUserError } from '@takaro/helpers';\n\nasync function main() {\n    const { player, gameServerId, arguments: args, module: mod, pog } = data;\n\n    // Check admin permission\n    if (!checkPermission(pog, 'ROULETTE_ADMIN')) {\n        throw new TakaroUserError('You do not have permission to reset roulette statistics!');\n    }\n\n    // Get target player\n    if (!args.player) {\n        throw new TakaroUserError('Please specify a player whose stats you want to reset.');\n    }\n\n    const targetPlayerId = args.player.playerId;\n    const targetPlayerName = args.player.name;\n\n    // Find stats to reset\n    const statsKey = 'roulette_stats';\n    const statsSearch = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [statsKey],\n            playerId: [targetPlayerId],\n            gameServerId: [gameServerId],\n            moduleId: [mod.moduleId],\n        },\n    });\n\n    if (statsSearch.data.data.length === 0) {\n        throw new TakaroUserError(`No roulette statistics found for ${targetPlayerName}.`);\n    }\n\n    // Delete the stats variable\n    await takaro.variable.variableControllerDelete(statsSearch.data.data[0].id);\n\n    // Also delete any cooldown variables\n    const cooldownKey = 'roulette_cooldown';\n    const cooldownSearch = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [cooldownKey],\n            playerId: [targetPlayerId],\n            gameServerId: [gameServerId],\n            moduleId: [mod.moduleId],\n        },\n    });\n\n    if (cooldownSearch.data.data.length > 0) {\n        await takaro.variable.variableControllerDelete(cooldownSearch.data.data[0].id);\n    }\n\n    await player.pm(`✅ Successfully reset roulette statistics for ${targetPlayerName}.`);\n\n    // Log the action\n    await takaro.gameserver.gameServerControllerExecuteCommand(gameServerId, {\n        command: `say [Admin] ${player.name} has reset ${targetPlayerName}'s roulette statistics.`,\n    });\n}\n\nawait main();",
          "arguments": [
            {
              "name": "player",
              "type": "string",
              "helpText": "Player whose stats to reset",
              "defaultValue": "",
              "position": 0
            }
          ]
        },
        {
          "name": "roulette",
          "trigger": "roulette",
          "helpText": "Place a bet and spin the roulette wheel.",
          "function": "import { takaro, data, checkPermission, TakaroUserError } from '@takaro/helpers';\n\nasync function main() {\n    const { player, gameServerId, arguments: args, module: mod, pog } = data;\n\n    // Define the roulette wheel number colors\n    const redNumbers = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36];\n    const blackNumbers = [2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35];\n\n    // Check permission\n    if (!checkPermission(pog, 'ROULETTE_PLAY')) {\n        throw new TakaroUserError('You do not have permission to play roulette!');\n    }\n\n    // Get the input from the first argument (bet type + possible number)\n    const betInput = args.betType ? args.betType.toLowerCase() : null;\n    const betAmount = Number(args.amount);\n\n    if (!betInput) {\n        throw new TakaroUserError('Please specify what you want to bet on!');\n    }\n\n    // Validate bet amount\n    if (isNaN(betAmount) || betAmount <= 0) {\n        throw new TakaroUserError('Please enter a valid positive bet amount!');\n    }\n\n    // Parse the bet type and number from the input\n    let betType = null;\n    let betNumber = null;\n    let betColor = null;\n\n    // Define valid bet types\n    const simpleBetTypes = ['red', 'black', 'even', 'odd', 'high', 'low'];\n\n    // Check if it's a simple bet type (red, black, etc.)\n    if (simpleBetTypes.includes(betInput)) {\n        betType = betInput;\n    }\n    // Check if it's a number bet (e.g., \"number17\" or just \"17\")\n    else if (betInput.startsWith('number')) {\n        betType = 'number';\n        betNumber = Number(betInput.substring(6)); // Extract number after \"number\"\n    }\n    // Check if it's red+number (e.g., \"red17\")\n    else if (betInput.startsWith('red') && betInput.length > 3) {\n        betType = 'colorednumber';\n        betColor = 'red';\n        betNumber = Number(betInput.substring(3)); // Extract number after \"red\"\n\n        // Validate the number is actually red on a real roulette wheel\n        if (!redNumbers.includes(betNumber)) {\n            throw new TakaroUserError(`Number ${betNumber} is not red on the roulette wheel. It's ${blackNumbers.includes(betNumber) ? 'black' : 'green (0)'}.`);\n        }\n    }\n    // Check if it's black+number (e.g., \"black17\")\n    else if (betInput.startsWith('black') && betInput.length > 5) {\n        betType = 'colorednumber';\n        betColor = 'black';\n        betNumber = Number(betInput.substring(5)); // Extract number after \"black\"\n\n        // Validate the number is actually black on a real roulette wheel\n        if (!blackNumbers.includes(betNumber)) {\n            throw new TakaroUserError(`Number ${betNumber} is not black on the roulette wheel. It's ${redNumbers.includes(betNumber) ? 'red' : 'green (0)'}.`);\n        }\n    }\n    // Check if it's just a number\n    else if (!isNaN(Number(betInput)) && Number(betInput) >= 0 && Number(betInput) <= 36) {\n        betType = 'number';\n        betNumber = Number(betInput);\n    }\n    // Unknown bet type\n    else {\n        throw new TakaroUserError(`Invalid bet type! Valid options: ${simpleBetTypes.join(', ')}, a number 0-36, or color+number (e.g., red17)`);\n    }\n\n    // Validate number if it's a number bet\n    if ((betType === 'number' || betType === 'colorednumber') &&\n        (isNaN(betNumber) || betNumber < 0 || betNumber > 36 || !Number.isInteger(betNumber))) {\n        throw new TakaroUserError('Please specify a valid number between 0 and 36!');\n    }\n\n    // Get minimum and maximum bet limits\n    const minimumBet = mod.userConfig.minimumBet;\n    let maximumBet = mod.userConfig.maximumBet;\n\n    // Check higher limits permission\n    const higherLimitsPermission = checkPermission(pog, 'ROULETTE_HIGHER_LIMITS');\n    if (higherLimitsPermission && higherLimitsPermission.count > 0) {\n        maximumBet *= higherLimitsPermission.count;\n    }\n\n    // Validate bet amount against limits\n    if (betAmount < minimumBet) {\n        throw new TakaroUserError(`The minimum bet is ${minimumBet} currency!`);\n    }\n    if (betAmount > maximumBet) {\n        throw new TakaroUserError(`The maximum bet is ${maximumBet} currency!`);\n    }\n\n    // Check if player is on cooldown\n    const cooldownTime = mod.userConfig.cooldownTime;\n    if (cooldownTime > 0) {  // Only check cooldown if it's enabled\n        const cooldownKey = 'roulette_cooldown';\n        const cooldownVar = await takaro.variable.variableControllerSearch({\n            filters: {\n                key: [cooldownKey],\n                playerId: [player.id],\n                gameServerId: [gameServerId],\n                moduleId: [mod.moduleId],\n            },\n        });\n\n        if (cooldownVar.data.data.length > 0) {\n            const lastSpinTime = new Date(cooldownVar.data.data[0].value).getTime();\n            const currentTime = Date.now();\n            const timeElapsed = currentTime - lastSpinTime;\n\n            if (timeElapsed < cooldownTime) {\n                const timeRemaining = Math.ceil((cooldownTime - timeElapsed) / 1000);\n                throw new TakaroUserError(`Please wait ${timeRemaining} seconds before spinning again!`);\n            }\n\n            // Update cooldown\n            await takaro.variable.variableControllerUpdate(cooldownVar.data.data[0].id, {\n                value: new Date().toISOString(),\n            });\n        } else {\n            // Create cooldown\n            await takaro.variable.variableControllerCreate({\n                key: cooldownKey,\n                value: new Date().toISOString(),\n                gameServerId,\n                moduleId: mod.moduleId,\n                playerId: player.id,\n            });\n        }\n    }\n\n    // Check if player has enough currency\n    const playerData = await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, player.id);\n    const currentBalance = playerData.data.data.currency;\n\n    if (currentBalance < betAmount) {\n        throw new TakaroUserError(`You need ${betAmount} currency to place this bet. You only have ${currentBalance}.`);\n    }\n\n    // Deduct the bet amount\n    await takaro.playerOnGameserver.playerOnGameServerControllerDeductCurrency(\n        gameServerId,\n        player.id,\n        {\n            currency: betAmount\n        }\n    );\n\n    // Set bet message based on bet type\n    let betMessage = '';\n    if (betType === 'colorednumber') {\n        betMessage = `${betColor} ${betNumber}`;\n    } else if (betType === 'number') {\n        betMessage = `number ${betNumber}`;\n    } else {\n        betMessage = betType;\n    }\n\n    // More compact spinning message\n    await player.pm(`🎰 Bet ${betAmount} on ${betMessage}... The wheel is spinning!`);\n\n    // Spin the wheel (generate a random number between 0 and 36)\n    const result = Math.floor(Math.random() * 37);\n    const isRed = redNumbers.includes(result);\n    const isBlack = blackNumbers.includes(result);\n    const isEven = result !== 0 && result % 2 === 0;\n    const isOdd = result % 2 === 1;\n    const isHigh = result >= 19 && result <= 36;\n    const isLow = result >= 1 && result <= 18;\n\n    // Display the result with Unicode for visual effect - more compact\n    let resultColor = isRed ? '🔴 red' : isBlack ? '⚫ black' : '🟢 green';\n    await player.pm(`🎲 Result: ${result} ${resultColor}`);\n\n    // Determine if player won\n    let playerWon = false;\n    let payoutMultiplier = 0;\n    const houseEdge = mod.userConfig.houseEdge / 100;  // Convert percentage to decimal\n\n    switch (betType) {\n        case 'red':\n            payoutMultiplier = 2 * (1 - houseEdge);\n            playerWon = isRed;\n            break;\n        case 'black':\n            payoutMultiplier = 2 * (1 - houseEdge);\n            playerWon = isBlack;\n            break;\n        case 'even':\n            payoutMultiplier = 2 * (1 - houseEdge);\n            playerWon = isEven;\n            break;\n        case 'odd':\n            payoutMultiplier = 2 * (1 - houseEdge);\n            playerWon = isOdd;\n            break;\n        case 'high':\n            payoutMultiplier = 2 * (1 - houseEdge);\n            playerWon = isHigh;\n            break;\n        case 'low':\n            payoutMultiplier = 2 * (1 - houseEdge);\n            playerWon = isLow;\n            break;\n        case 'number':\n            payoutMultiplier = 36 * (1 - houseEdge);\n            playerWon = (result === betNumber);\n            break;\n        case 'colorednumber':\n            // For colored number bets, we need both the number AND color to match\n            payoutMultiplier = 72 * (1 - houseEdge); // Double the normal number bet payout\n            playerWon = (result === betNumber) &&\n                ((betColor === 'red' && isRed) || (betColor === 'black' && isBlack));\n            break;\n    }\n\n    // Update player stats\n    await updatePlayerStats(player.id, gameServerId, mod.moduleId, betAmount, playerWon);\n\n    // Pay out winnings if player won\n    if (playerWon) {\n        const winnings = Math.floor(betAmount * payoutMultiplier);\n        await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(\n            gameServerId,\n            player.id,\n            {\n                currency: winnings\n            }\n        );\n\n        // More compact win message\n        await player.pm(`💰 You win! +${winnings} currency! 💰`);\n\n        // Broadcast big wins to everyone\n        if (winnings >= 1000) {\n            await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n                message: `🎰 ${player.name} just won ${winnings} currency at roulette with a bet on ${betMessage}!`\n            });\n        }\n    } else {\n        // More compact loss message\n        await player.pm(`❌ You lose! Better luck next time!`);\n    }\n\n    // Get updated balance\n    const updatedPlayerData = await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, player.id);\n    await player.pm(`Balance: ${updatedPlayerData.data.data.currency} currency`);\n}\n\nasync function updatePlayerStats(playerId, gameServerId, moduleId, betAmount, won) {\n    const statsKey = 'roulette_stats';\n    const statsSearch = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [statsKey],\n            playerId: [playerId],\n            gameServerId: [gameServerId],\n            moduleId: [moduleId],\n        },\n    });\n\n    let stats = {\n        played: 0,\n        won: 0,\n        lost: 0,\n        totalWagered: 0,\n    };\n\n    if (statsSearch.data.data.length > 0) {\n        stats = JSON.parse(statsSearch.data.data[0].value);\n        await takaro.variable.variableControllerUpdate(statsSearch.data.data[0].id, {\n            value: JSON.stringify({\n                played: stats.played + 1,\n                won: stats.won + (won ? 1 : 0),\n                lost: stats.lost + (won ? 0 : 1),\n                totalWagered: stats.totalWagered + betAmount,\n            }),\n        });\n    } else {\n        await takaro.variable.variableControllerCreate({\n            key: statsKey,\n            value: JSON.stringify({\n                played: 1,\n                won: won ? 1 : 0,\n                lost: won ? 0 : 1,\n                totalWagered: betAmount,\n            }),\n            gameServerId,\n            moduleId: moduleId,\n            playerId: playerId,\n        });\n    }\n}\n\nawait main();",
          "arguments": [
            {
              "name": "amount",
              "type": "number",
              "helpText": "Amount to bet",
              "defaultValue": "",
              "position": 1
            },
            {
              "name": "betType",
              "type": "string",
              "helpText": "Type of bet (red, black, even, odd, high, low) or a number 0-36",
              "defaultValue": "",
              "position": 0
            }
          ]
        },
        {
          "name": "rouletterules",
          "trigger": "rouletterules",
          "helpText": "Shows the rules and payouts for roulette.",
          "function": "import { takaro, data } from '@takaro/helpers';\n\nasync function main() {\n    const { player, module: mod } = data;\n\n    const minimumBet = mod.userConfig.minimumBet;\n    const maximumBet = mod.userConfig.maximumBet;\n    const houseEdge = mod.userConfig.houseEdge;\n\n    // Part 1: Introduction and basic info\n    const rules1 = [\n        `🎰 🎲 ROULETTE RULES 🎲 🎰`,\n        '',\n        `Minimum bet: ${minimumBet} currency`,\n        `Maximum bet: ${maximumBet} currency`,\n        `House edge: ${houseEdge}%`,\n    ];\n\n    // Part 2: Bet types\n    const rules2 = [\n        'BET TYPES AND PAYOUTS:',\n        '',\n        '🔴 RED - Pays 1:1 - Bet on the ball landing on a red number',\n        '⚫ BLACK - Pays 1:1 - Bet on the ball landing on a black number',\n        '🔢 EVEN - Pays 1:1 - Bet on the ball landing on an even number (not 0)',\n        '🔢 ODD - Pays 1:1 - Bet on the ball landing on an odd number',\n        '⬆️ HIGH - Pays 1:1 - Bet on the ball landing on numbers 19-36',\n        '⬇️ LOW - Pays 1:1 - Bet on the ball landing on numbers 1-18',\n        '🎯 NUMBER - Pays 35:1 - Bet on a specific number (0-36)',\n        '🎯 COLOR+NUMBER - Pays 70:1 - Bet on a specific number AND color',\n    ];\n\n    // Part 3: Examples\n    const rules3 = [\n        'EXAMPLES:',\n        '/roulette red 100 - Bet 100 on red',\n        '/roulette 17 50 - Bet 50 on number 17',\n        '/roulette black 200 - Bet 200 on black',\n        '/roulette red17 500 - Bet 500 on red 17',\n        '',\n        'Use /roulettestats to view your statistics!'\n    ];\n\n    // Send messages with a slight delay between them\n    await player.pm(rules1.join('\\n'));\n    await player.pm(rules2.join('\\n'));\n    await player.pm(rules3.join('\\n'));\n}\n\nawait main();",
          "arguments": []
        },
        {
          "name": "roulettestats",
          "trigger": "roulettestats",
          "helpText": "Shows your roulette statistics",
          "function": "import { takaro, data } from '@takaro/helpers';\n\nasync function main() {\n    const { player, gameServerId, module: mod } = data;\n\n    const statsKey = 'roulette_stats';\n    const statsSearch = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: [statsKey],\n            playerId: [player.id],\n            gameServerId: [gameServerId],\n            moduleId: [mod.moduleId],\n        },\n    });\n\n    if (statsSearch.data.data.length === 0) {\n        await player.pm(\"🎰 You haven't played any roulette games yet!\");\n        return;\n    }\n\n    const stats = JSON.parse(statsSearch.data.data[0].value);\n    const winRate = stats.played > 0 ? ((stats.won / stats.played) * 100).toFixed(1) : 0;\n\n    // More compact stats display\n    const statsDisplay = [\n        `🎰 ROULETTE STATS 🎰`,\n        `Games: ${stats.played} (${stats.won} wins, ${stats.lost} losses)`,\n        `Win rate: ${winRate}%`,\n        `Total wagered: ${stats.totalWagered} currency`,\n        `🍀 Good luck on your next spin!`\n    ];\n\n    await player.pm(statsDisplay.join('\\n'));\n}\n\nawait main();",
          "arguments": []
        }
      ],
      "hooks": [],
      "cronJobs": [],
      "functions": [],
      "permissions": [
        {
          "permission": "ROULETTE_PLAY",
          "friendlyName": "Play Roulette",
          "description": "Allows the player to place bets and play roulette.",
          "canHaveCount": false
        },
        {
          "permission": "ROULETTE_HIGHER_LIMITS",
          "friendlyName": "Higher Betting Limits",
          "description": "Allows the player to exceed the normal maximum bet. Count is multiplier for max bet.",
          "canHaveCount": true
        },
        {
          "permission": "ROULETTE_ADMIN",
          "friendlyName": "Roulette Admin",
          "description": "Allows resetting player statistics and other administrative functions.",
          "canHaveCount": false
        }
      ]
    }
  ]
}