{
  "name": "Hangman",
  "author": "limon",
  "supportedGames": [
    "all"
  ],
  "takaroVersion": "v0.0.21",
  "versions": [
    {
      "tag": "latest",
      "description": "A classic Hangman game where players guess letters to reveal a hidden word. This interactive module adds a fun, competitive element to your server with full economy integration.\n\n## Key Functionality\n\n* **Game Hosting:** Players can create new Hangman games by specifying a secret word or phrase for others to guess.\n* **Player Participation:** Multiple players can join ongoing games, creating a social, competitive experience.\n* **Letter Guessing:** Players take turns guessing letters or attempting the full word, with visual feedback after each guess.\n* **Progressive Difficulty:** Wrong guesses accumulate toward a maximum limit, creating tension as the game progresses.\n* **Economic Integration:** Full support for your server's economy with:\n  * Hosting fees for creating games\n  * Entry fees for joining games\n  * Optional costs per guess\n  * Currency rewards for winners\n* **Game Management:** Commands for checking game status, revealing answers, or canceling games as needed.\n\n## How to Use\n\n1. **Configuration:**\n   * `maxWrongGuesses`: Set the number of incorrect guesses allowed before the game ends.\n   * `minWordLength` and `maxWordLength`: Define valid word length parameters.\n   * `entryFee`: Cost for players to join a game.\n   * `hostingFee`: Cost to start a new game.\n   * `guessPrice`: Optional cost per letter guess.\n   * `winReward`: Currency awarded to successful players.\n   * `hostReward`: Currency awarded to hosts when no player solves the puzzle.\n   * `gameTimeout`: Maximum duration a game can run.\n\n2. **Commands:**\n   * `/hangmanstart [word]`: Create a new game or start a waiting game.\n   * `/hangmanjoin`: Join an existing game that hasn't started.\n   * `/hangmanguess [letter/word]`: Make a guess during an active game.\n   * `/hangmanstatus`: Check the current state of the game.\n   * `/hangmanreveal`: Reveal the answer and end the game (host/admin only).\n   * `/hangmancancel`: Cancel the current game (host/admin only).\n\n3. **Permissions:**\n   * `HANGMAN_HOST`: Allows players to create and host games.\n   * `HANGMAN_PLAY`: Allows players to join games and make guesses.\n   * `HANGMAN_ADMIN`: Grants administrative control over all games.\n\n## Game Flow\n\n1. A player with hosting permissions creates a new game with a secret word.\n2. Other players join the waiting game.\n3. The host starts the game when enough players have joined.\n4. Players take turns guessing letters or the complete word.\n5. With each guess, the game displays the current word state and remaining guesses.\n6. The game ends when:\n   * A player correctly guesses the word (player wins the pot)\n   * Players reach the maximum wrong guesses (host wins the pot)\n   * An admin or host cancels or reveals the game\n\n## Important Considerations\n\n* Configuring appropriate fees and rewards helps balance your server economy.\n* The word validation ensures fair gameplay (letters and spaces only).\n* Players cannot join games that have already started.\n* The host cannot participate in guessing since they know the word.\n* Currency features require the economy system to be enabled.",
      "configSchema": "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"required\":[],\"additionalProperties\":false,\"properties\":{\"maxWrongGuesses\":{\"title\":\"maxWrongGuesses\",\"description\":\"Maximum number of wrong guesses allowed before the game ends.\",\"default\":6,\"type\":\"number\",\"minimum\":1,\"maximum\":100},\"winReward\":{\"title\":\"winReward\",\"description\":\"Amount of currency to reward the player who guesses the word correctly.\",\"default\":100,\"type\":\"number\"},\"hostReward\":{\"title\":\"hostReward\",\"description\":\"Amount of currency to reward the player who hosts the game if nobody guesses the word.\",\"default\":50,\"type\":\"number\"},\"gameTimeout\":{\"title\":\"gameTimeout\",\"description\":\"Maximum time a game can run before automatically ending.\",\"default\":60000,\"x-component\":\"duration\",\"type\":\"number\"},\"minWordLength\":{\"title\":\"minWordLength\",\"description\":\"Minimum length of words that can be used in the game.\",\"default\":4,\"type\":\"number\",\"minimum\":3},\"maxWordLength\":{\"title\":\"maxWordLength\",\"description\":\"Maximum length of words that can be used in the game.\",\"default\":15,\"type\":\"number\",\"minimum\":5},\"entryFee\":{\"title\":\"entryFee\",\"description\":\"Cost to join a Hangman game\",\"default\":10,\"type\":\"number\"},\"guessPrice\":{\"title\":\"guessPrice\",\"description\":\"Cost per letter guess\",\"default\":0,\"type\":\"number\"},\"hostingFee\":{\"title\":\"hostingFee\",\"description\":\"Cost to create and host a new Hangman game\",\"default\":20,\"type\":\"number\"}}}",
      "uiSchema": "{}",
      "commands": [
        {
          "name": "hangmanStart",
          "trigger": "hangmanstart",
          "helpText": "No help text available",
          "function": "// commands/hangmanStart.js\nimport { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\n\nasync function main() {\n    const { player, gameServerId, module: mod, arguments: args, pog } = data;\n\n    try {\n        // Check if player has permission to host games\n        if (!checkPermission(pog, 'HANGMAN_HOST')) {\n            throw new TakaroUserError('You do not have permission to host Hangman games!');\n        }\n\n        // Search for existing game\n        const gameVars = await takaro.variable.variableControllerSearch({\n            filters: {\n                key: ['hangman_game_state'],\n                gameServerId: [gameServerId],\n                moduleId: [mod.moduleId]\n            }\n        });\n\n        // Check if there's an existing game\n        if (gameVars.data.data.length === 0) {\n            // No existing game\n            if (!args.word) {\n                throw new TakaroUserError('You must provide a word or phrase to start a Hangman game!');\n            }\n\n            // Word provided, create new game\n            const word = args.word.toLowerCase().trim();\n\n            // Validate word\n            if (word.length < mod.userConfig.minWordLength) {\n                throw new TakaroUserError(`Word must be at least ${mod.userConfig.minWordLength} characters long!`);\n            }\n\n            if (word.length > mod.userConfig.maxWordLength) {\n                throw new TakaroUserError(`Word must be no more than ${mod.userConfig.maxWordLength} characters long!`);\n            }\n\n            if (!/^[a-z ]+$/.test(word)) {\n                throw new TakaroUserError('Word can only contain letters and spaces!');\n            }\n\n            // Check if there's a hosting fee and process it\n            let hostingFee = mod.userConfig.hostingFee || 0;\n            if (hostingFee > 0) {\n                try {\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 < hostingFee) {\n                        throw new TakaroUserError(`You need ${hostingFee} currency to host a Hangman game. You only have ${currentBalance}.`);\n                    }\n\n                    // Deduct the hosting fee\n                    await takaro.playerOnGameserver.playerOnGameServerControllerDeductCurrency(\n                        gameServerId,\n                        player.id,\n                        {\n                            currency: hostingFee\n                        }\n                    );\n\n                    await player.pm(`You paid ${hostingFee} currency to host this Hangman game.`);\n                } catch (error) {\n                    if (error instanceof TakaroUserError) throw error;\n                    console.error('Economy error:', error);\n                    throw new TakaroUserError('Failed to process hosting fee. Economy system might be disabled.');\n                }\n            }\n\n            // Create game state\n            const gameState = {\n                word: word,\n                hostId: player.id,\n                hostName: player.name,\n                players: [{ id: player.id, name: player.name }],\n                guessedLetters: [],\n                wrongLetters: [],\n                wrongGuesses: 0,\n                maxWrongGuesses: mod.userConfig.maxWrongGuesses,\n                startTime: Date.now(),\n                gameTimeout: mod.userConfig.gameTimeout,\n                active: false,\n                started: false,\n                pot: hostingFee // Initialize pot with the hosting fee\n            };\n\n            // Create variable\n            await takaro.variable.variableControllerCreate({\n                key: 'hangman_game_state',\n                value: JSON.stringify(gameState),\n                gameServerId,\n                moduleId: mod.moduleId\n            });\n\n            // Send messages\n            await player.pm(`You've started a new Hangman game with the word: ${word}`);\n            await player.pm('As the host, you cannot participate in guessing since you know the word.');\n            await player.pm('Players can join by typing /hangmanjoin');\n            await player.pm('Once players have joined, use /hangmanstart again (with the word) to begin the game!');\n\n            await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n                message: `${player.name} has created a new Hangman game! Type /hangmanjoin to participate.`\n            });\n        }\n        else {\n            // Existing game, try to activate it\n            const gameState = JSON.parse(gameVars.data.data[0].value);\n\n            // If game already started\n            if (gameState.started) {\n                throw new TakaroUserError('A Hangman game is already in progress! Use /hangmanstatus to see the current game.');\n            }\n\n            // If player is not host\n            if (gameState.hostId !== player.id) {\n                throw new TakaroUserError('You are not the host of this game! Only the host can start it.');\n            }\n\n            // If not enough players\n            if (gameState.players.length < 2) {\n                throw new TakaroUserError('At least one other player must join before you can start the game!');\n            }\n\n            // Activate game\n            gameState.active = true;\n            gameState.started = true;\n\n            // Update variable\n            await takaro.variable.variableControllerUpdate(gameVars.data.data[0].id, {\n                value: JSON.stringify(gameState)\n            });\n\n            // Announce game start\n            await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n                message: `The Hangman game has begun! Use /hangmanguess [letter] to make guesses. Current pot: ${gameState.pot} currency.`\n            });\n        }\n    } catch (error) {\n        if (error instanceof TakaroUserError) {\n            throw error;\n        }\n        console.error('Error in hangmanStart:', error);\n        throw new TakaroUserError('An error occurred while processing your command.');\n    }\n}\n\nawait main();",
          "arguments": [
            {
              "name": "word",
              "type": "string",
              "helpText": "The word or phrase players will try to guess.",
              "defaultValue": "",
              "position": 0
            }
          ]
        },
        {
          "name": "hangmanJoin",
          "trigger": "hangmanjoin",
          "helpText": "No help text available",
          "function": "// commands/hangmanJoin.js\nimport { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\n\nasync function main() {\n    const { player, gameServerId, module: mod, pog } = data;\n\n    // Check if player has permission to play\n    if (!checkPermission(pog, 'HANGMAN_PLAY')) {\n        throw new TakaroUserError('You do not have permission to play Hangman games!');\n    }\n\n    // Check if there's a game to join\n    const gameVars = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: ['hangman_game_state'],\n            gameServerId: [gameServerId],\n            moduleId: [mod.moduleId]\n        }\n    });\n\n    if (gameVars.data.data.length === 0) {\n        throw new TakaroUserError('There is no Hangman game in progress! Use /hangmanstart to create one.');\n    }\n\n    const gameState = JSON.parse(gameVars.data.data[0].value);\n\n    // Check if player is the host\n    if (player.id === gameState.hostId) {\n        throw new TakaroUserError('You are the host of this game! As the host, you cannot participate in guessing since you know the word.');\n    }\n\n    // Check if game has already started\n    if (gameState.started) {\n        throw new TakaroUserError('The game has already started! Wait for the next game.');\n    }\n\n    // Check if player is already in the game\n    if (gameState.players.some(p => p.id === player.id)) {\n        throw new TakaroUserError('You have already joined this game!');\n    }\n\n    // Process entry fee if configured\n    const entryFee = mod.userConfig.entryFee || 0;\n    if (entryFee > 0) {\n        try {\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 < entryFee) {\n                throw new TakaroUserError(`You need ${entryFee} currency to join this game. You only have ${currentBalance}.`);\n            }\n\n            // Deduct the entry fee\n            await takaro.playerOnGameserver.playerOnGameServerControllerDeductCurrency(\n                gameServerId,\n                player.id,\n                {\n                    currency: entryFee\n                }\n            );\n\n            // Add to the pot\n            gameState.pot += entryFee;\n\n            await player.pm(`You paid ${entryFee} currency to join the game. Current pot: ${gameState.pot}`);\n        } catch (error) {\n            if (error instanceof TakaroUserError) throw error;\n            console.error('Economy error:', error);\n            throw new TakaroUserError('Failed to process entry fee. Economy system might be disabled.');\n        }\n    }\n\n    // Add player to game\n    gameState.players.push({ id: player.id, name: player.name });\n\n    // Save updated game state\n    await takaro.variable.variableControllerUpdate(gameVars.data.data[0].id, {\n        value: JSON.stringify(gameState)\n    });\n\n    // Notify players\n    await player.pm('You have joined the Hangman game!');\n\n    // Notify the host - using the correct API call\n    const hostPlayer = await takaro.player.playerControllerGetOne(gameState.hostId);\n    if (hostPlayer && hostPlayer.data && hostPlayer.data.data) {\n        const hostPog = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({\n            filters: {\n                playerId: [gameState.hostId],\n                gameServerId: [gameServerId],\n                online: [true]\n            }\n        });\n\n        if (hostPog.data.data.length > 0) {\n            // Host is online, send a PM\n            await takaro.gameserver.gameServerControllerExecuteCommand(gameServerId, {\n                command: `pm ${hostPog.data.data[0].steamId} \"${player.name} has joined your Hangman game! ${gameState.players.length} players are now participating.\"`\n            });\n        }\n    }\n\n    // Send announcement\n    await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n        message: `${player.name} has joined the Hangman game! (${gameState.players.length} players)`\n    });\n}\n\nawait main();",
          "arguments": []
        },
        {
          "name": "hangmanGuess",
          "trigger": "hangmanguess",
          "helpText": "No help text available",
          "function": "// commands/hangmanGuess.js\nimport { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\n\nasync function main() {\n  const { player, gameServerId, module: mod, arguments: args, pog } = data;\n\n  // Check if player has permission to play\n  if (!checkPermission(pog, 'HANGMAN_PLAY')) {\n    throw new TakaroUserError('You do not have permission to play Hangman games!');\n  }\n\n  // Check if there's a game in progress\n  const gameVars = await takaro.variable.variableControllerSearch({\n    filters: {\n      key: ['hangman_game_state'],\n      gameServerId: [gameServerId],\n      moduleId: [mod.moduleId]\n    }\n  });\n\n  if (gameVars.data.data.length === 0) {\n    throw new TakaroUserError('There is no Hangman game in progress!');\n  }\n\n  const gameState = JSON.parse(gameVars.data.data[0].value);\n\n  // Check if the game is active\n  if (!gameState.active) {\n    throw new TakaroUserError('The game has not started yet!');\n  }\n\n  // Check if player is participating\n  if (!gameState.players.some(p => p.id === player.id)) {\n    throw new TakaroUserError('You are not participating in this game! Wait for the next one.');\n  }\n\n  // Check if player is the host\n  if (player.id === gameState.hostId) {\n    throw new TakaroUserError('As the game host, you cannot participate in guessing since you know the word!');\n  }\n\n  // Validate guess\n  if (!args.letter) {\n    throw new TakaroUserError('You must provide a letter or word to guess!');\n  }\n\n  // Process guess fee\n  const guessPrice = mod.userConfig.guessPrice || 0;\n  if (guessPrice > 0) {\n    try {\n      // Check if player has enough currency\n      const playerData = await takaro.playerOnGameserver.playerOnGameServerControllerGetOne(gameServerId, player.id); const currentBalance = playerData.data.data.currency;\n\n      if (currentBalance < guessPrice) {\n        throw new TakaroUserError(`You need ${guessPrice} currency to make a guess. You only have ${currentBalance}.`);\n      }\n\n      // Deduct the guess fee\n      await takaro.playerOnGameserver.playerOnGameServerControllerDeductCurrency(\n        gameServerId,\n        player.id,\n        {\n          currency: guessPrice\n        }\n      );\n\n      // Add to the pot\n      gameState.pot += guessPrice;\n\n      await player.pm(`You paid ${guessPrice} currency to make a guess. Current pot: ${gameState.pot}`);\n    } catch (error) {\n      if (error instanceof TakaroUserError) throw error;\n      console.error('Economy error:', error);\n      throw new TakaroUserError('Failed to process guess fee. Economy system might be disabled.');\n    }\n  }\n\n  const guess = args.letter.toLowerCase().trim();\n\n  // Handle full word guess\n  if (guess.length > 1) {\n    // Check if the guess matches the full word\n    if (guess === gameState.word) {\n      // Player correctly guessed the whole word!\n      await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n        message: `Congratulations! ${player.name} has correctly guessed the word: ${gameState.word}`\n      });\n\n      // Award pot to winner\n      if (gameState.pot > 0) {\n        try {\n          await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(\n            gameServerId,\n            player.id,\n            {\n              currency: gameState.pot\n            }\n          );\n\n          await player.pm(`Congratulations! You won the entire pot of ${gameState.pot} currency!`);\n          await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n            message: `${player.name} won the Hangman game and collected the pot of ${gameState.pot} currency!`\n          });\n        } catch (error) {\n          console.error('Error granting currency:', error);\n        }\n      }\n\n      // Clear game state\n      await takaro.variable.variableControllerDelete(gameVars.data.data[0].id);\n      return;\n    } else {\n      // Wrong word guess - counts as a wrong guess\n      gameState.wrongGuesses++;\n\n      await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n        message: `${player.name} guessed \"${guess}\" - That's not the word! (${gameState.wrongGuesses}/${gameState.maxWrongGuesses})`\n      });\n\n      // Check if game over due to too many wrong guesses\n      if (gameState.wrongGuesses >= gameState.maxWrongGuesses) {\n        await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n          message: `Game over! The word was: ${gameState.word}`\n        });\n\n        // Award pot to host\n        if (gameState.pot > 0) {\n          try {\n            await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(\n              gameServerId,\n              gameState.hostId,\n              {\n                currency: gameState.pot\n              }\n            );\n\n            await takaro.player.playerControllerPm(gameState.hostId, {\n              message: `You received the pot of ${gameState.pot} currency because nobody solved your Hangman puzzle!`\n            });\n            await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n              message: `${gameState.hostName} receives the ${gameState.pot} currency pot as nobody solved the puzzle!`\n            });\n          } catch (error) {\n            console.error('Error granting currency:', error);\n          }\n        }\n\n        // Clear game state\n        await takaro.variable.variableControllerDelete(gameVars.data.data[0].id);\n        return;\n      }\n\n      // Update game state\n      await takaro.variable.variableControllerUpdate(gameVars.data.data[0].id, {\n        value: JSON.stringify(gameState)\n      });\n\n      // Render current state\n      await renderAndSendGameState(gameState, gameServerId);\n      return;\n    }\n  }\n\n  // Handle single letter guess\n  if (guess.length !== 1 || !/^[a-z]$/.test(guess)) {\n    throw new TakaroUserError('You can only guess a single letter or the entire word!');\n  }\n\n  // Check if letter has already been guessed\n  if (gameState.guessedLetters.includes(guess)) {\n    throw new TakaroUserError(`The letter \"${guess}\" has already been guessed!`);\n  }\n\n  // Record the guess\n  gameState.guessedLetters.push(guess);\n\n  // Check if guess is correct\n  if (gameState.word.includes(guess)) {\n    // Correct guess\n    await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n      message: `${player.name} correctly guessed \"${guess}\"!`\n    });\n  } else {\n    // Wrong guess\n    gameState.wrongGuesses++;\n    gameState.wrongLetters.push(guess);\n    await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n      message: `${player.name} guessed \"${guess}\" - not in the word! (${gameState.wrongGuesses}/${gameState.maxWrongGuesses})`\n    });\n  }\n\n  // Render current game state\n  await renderAndSendGameState(gameState, gameServerId);\n\n  // Check if the game is over\n  const wordLetters = new Set(gameState.word.replace(/\\s/g, '').split(''));\n  const correctlyGuessed = gameState.guessedLetters.filter(letter => gameState.word.includes(letter));\n  const allLettersGuessed = [...wordLetters].every(letter => correctlyGuessed.includes(letter));\n\n  if (allLettersGuessed) {\n    // Player won!\n    await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n      message: `Congratulations! The word has been solved: ${gameState.word}`\n    });\n\n    // Award pot to winner\n    if (gameState.pot > 0) {\n      try {\n        await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(\n          gameServerId,\n          player.id,\n          {\n            currency: gameState.pot\n          }\n        );\n\n        await player.pm(`Congratulations! You won the entire pot of ${gameState.pot} currency!`);\n        await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n          message: `${player.name} won the Hangman game and collected the pot of ${gameState.pot} currency!`\n        });\n      } catch (error) {\n        console.error('Error granting currency:', error);\n      }\n    }\n\n    // Clear game state\n    await takaro.variable.variableControllerDelete(gameVars.data.data[0].id);\n\n  } else if (gameState.wrongGuesses >= gameState.maxWrongGuesses) {\n    // Game over - too many wrong guesses\n    await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n      message: `Game over! The word was: ${gameState.word}`\n    });\n\n    // Award pot to host\n    if (gameState.pot > 0) {\n      try {\n        await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(\n          gameServerId,\n          gameState.hostId,\n          {\n            currency: gameState.pot\n          }\n        );\n\n        await takaro.player.playerControllerPm(gameState.hostId, {\n          message: `You received the pot of ${gameState.pot} currency because nobody solved your Hangman puzzle!`\n        });\n        await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n          message: `${gameState.hostName} receives the ${gameState.pot} currency pot as nobody solved the puzzle!`\n        });\n      } catch (error) {\n        console.error('Error granting currency:', error);\n      }\n    }\n\n    // Clear game state\n    await takaro.variable.variableControllerDelete(gameVars.data.data[0].id);\n  } else {\n    // Game continues - save updated state\n    await takaro.variable.variableControllerUpdate(gameVars.data.data[0].id, {\n      value: JSON.stringify(gameState)\n    });\n  }\n}\n\n// Helper function to render and send game state\nasync function renderAndSendGameState(gameState, gameServerId) {\n  const { word, guessedLetters, wrongGuesses, maxWrongGuesses, wrongLetters } = gameState;\n\n  // Create the word display with correctly guessed letters shown\n  const wordDisplay = word.split('').map(letter => {\n    if (letter === ' ') return ' ';\n    return guessedLetters.includes(letter.toLowerCase()) ? letter : '_';\n  }).join(' ');\n\n  // Create a dynamic status based on current progress\n  const percentComplete = Math.floor((wrongGuesses / maxWrongGuesses) * 100);\n  let hangmanStatus;\n\n  if (wrongGuesses === 0) {\n    hangmanStatus = \"Hangman: No wrong guesses yet\";\n  } else {\n    hangmanStatus = `Hangman: ${wrongGuesses}/${maxWrongGuesses} wrong guesses (${percentComplete}% to game over)`;\n  }\n\n  const wrongGuessesText = `Wrong guesses: ${wrongLetters.join(' ') || 'none'}`;\n  const guessedLettersText = `Letters guessed: ${guessedLetters.join(' ') || 'none'}`;\n  const potInfo = `Current pot: ${gameState.pot} currency`;\n\n  const display = `${hangmanStatus}\\n${wordDisplay}\\n${wrongGuessesText}\\n${guessedLettersText}\\n${potInfo}`;\n\n  await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n    message: display\n  });\n}\n\nawait main();",
          "arguments": [
            {
              "name": "letter",
              "type": "string",
              "helpText": "The letter you want to guess.",
              "defaultValue": "",
              "position": 0
            }
          ]
        },
        {
          "name": "hangmanStatus",
          "trigger": "hangmanstatus",
          "helpText": "No help text available",
          "function": "// commands/hangmanStatus.js\nimport { takaro, data, TakaroUserError } from '@takaro/helpers';\nimport { getGameState, renderHangmanState } from './utils.js';\n\nasync function main() {\n    const { player, gameServerId, module: mod } = data;\n\n    // Check if there's a game in progress\n    const gameState = await getGameState(gameServerId, mod.moduleId);\n    if (!gameState) {\n        throw new TakaroUserError('There is no Hangman game in progress!');\n    }\n\n    // Show the game status\n    if (!gameState.started) {\n        const playerList = gameState.players.map(p => p.name).join(', ');\n        await player.pm(`Hangman game hosted by ${gameState.hostName} is waiting to start.`);\n        await player.pm(`Players (${gameState.players.length}): ${playerList}`);\n        await player.pm(`Current pot: ${gameState.pot || 0} currency`);\n        await player.pm('Type /hangmanjoin to participate!');\n    } else {\n        // Show the current state of the game\n        const display = renderHangmanState(gameState);\n        await player.pm(`Current Hangman Game Status:`);\n        await player.pm(display);\n        await player.pm(`Players (${gameState.players.length}): ${gameState.players.map(p => p.name).join(', ')}`);\n        await player.pm(`Current pot: ${gameState.pot || 0} currency`);\n    }\n}\n\nawait main();",
          "arguments": []
        },
        {
          "name": "hangmanReveal",
          "trigger": "hangmanreveal",
          "helpText": "No help text available",
          "function": "// commands/hangmanReveal.js\nimport { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\nimport { getGameState, saveGameState } from './utils.js';\n\nasync function main() {\n    const { player, gameServerId, module: mod, pog } = data;\n\n    // Check if there's a game in progress\n    const gameState = await getGameState(gameServerId, mod.moduleId);\n    if (!gameState) {\n        throw new TakaroUserError('There is no Hangman game in progress!');\n    }\n\n    // Check if player has permission to reveal\n    const isHost = gameState.hostId === player.id;\n    const isAdmin = checkPermission(pog, 'HANGMAN_ADMIN');\n\n    if (!isHost && !isAdmin) {\n        throw new TakaroUserError('Only the game host or an admin can reveal the word!');\n    }\n\n    // Reveal the word and end the game\n    await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n        message: `${player.name} has revealed the word: ${gameState.word}`\n    });\n\n    // Clear game state\n    await clearGameState(gameServerId, mod.moduleId);\n    return { success: true };\n}\n\nawait main();",
          "arguments": []
        },
        {
          "name": "hangmanCancel",
          "trigger": "hangmancancel",
          "helpText": "No help text available",
          "function": "// commands/hangmanCancel.js\nimport { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\nimport { getGameState, clearGameState } from './utils.js';\n\nasync function main() {\n    const { player, gameServerId, module: mod, pog } = data;\n\n    // Check if there's a game to cancel\n    const gameState = await getGameState(gameServerId, mod.moduleId);\n    if (!gameState) {\n        throw new TakaroUserError('There is no Hangman game in progress!');\n    }\n\n    // Check if player has permission to cancel\n    const isHost = gameState.hostId === player.id;\n    const isAdmin = checkPermission(pog, 'HANGMAN_ADMIN');\n\n    if (!isHost && !isAdmin) {\n        throw new TakaroUserError('Only the game host or an admin can cancel the game!');\n    }\n\n    // Cancel the game\n    await clearGameState(gameServerId, mod.moduleId);\n\n    // Announce cancellation\n    await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n        message: `${player.name} has cancelled the current Hangman game.`\n    });\n\n    if (gameState.started) {\n        await takaro.gameserver.gameServerControllerSendMessage(gameServerId, {\n            message: `The word was: ${gameState.word}`\n        });\n    }\n\n\n}\n\nawait main()",
          "arguments": []
        }
      ],
      "hooks": [],
      "cronJobs": [],
      "functions": [
        {
          "name": "utils",
          "description": "",
          "function": "// functions/utils.js\nimport { takaro } from '@takaro/helpers';\n\n/**\n * Gets the current hangman game state from variables\n */\nexport async function getGameState(gameServerId, moduleId) {\n    const gameVars = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: ['hangman_game_state'],\n            gameServerId: [gameServerId],\n            moduleId: [moduleId]\n        }\n    });\n\n    if (gameVars.data.data.length === 0) {\n        return null;\n    }\n\n    return JSON.parse(gameVars.data.data[0].value);\n}\n\n/**\n * Saves the current hangman game state to variables\n */\nexport async function saveGameState(gameState, gameServerId, moduleId) {\n    const gameVars = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: ['hangman_game_state'],\n            gameServerId: [gameServerId],\n            moduleId: [moduleId]\n        }\n    });\n\n    if (gameVars.data.data.length === 0) {\n        await takaro.variable.variableControllerCreate({\n            key: 'hangman_game_state',\n            value: JSON.stringify(gameState),\n            gameServerId,\n            moduleId\n        });\n    } else {\n        await takaro.variable.variableControllerUpdate(gameVars.data.data[0].id, {\n            value: JSON.stringify(gameState)\n        });\n    }\n}\n\n/**\n * Clears the current hangman game state\n */\nexport async function clearGameState(gameServerId, moduleId) {\n    const gameVars = await takaro.variable.variableControllerSearch({\n        filters: {\n            key: ['hangman_game_state'],\n            gameServerId: [gameServerId],\n            moduleId: [moduleId]\n        }\n    });\n\n    if (gameVars.data.data.length > 0) {\n        await takaro.variable.variableControllerDelete(gameVars.data.data[0].id);\n    }\n}\n\n/**\n * Renders the current hangman state for display\n */\nexport function renderHangmanState(gameState) {\n    const { word, guessedLetters, wrongGuesses, maxWrongGuesses, wrongLetters } = gameState;\n\n    // Create the word display with correctly guessed letters shown\n    const wordDisplay = word.split('').map(letter => {\n        if (letter === ' ') return ' ';\n        return guessedLetters.includes(letter.toLowerCase()) ? letter : '_';\n    }).join(' ');\n\n    // Create a dynamic status based on current progress\n    const percentComplete = Math.floor((wrongGuesses / maxWrongGuesses) * 100);\n    let hangmanStatus;\n\n    if (wrongGuesses === 0) {\n        hangmanStatus = \"Hangman: No wrong guesses yet\";\n    } else {\n        hangmanStatus = `Hangman: ${wrongGuesses}/${maxWrongGuesses} wrong guesses (${percentComplete}% to game over)`;\n    }\n\n    const wrongGuessesText = `Wrong guesses: ${wrongLetters ? wrongLetters.join(' ') : 'none'}`;\n    const guessedLettersText = `Letters guessed: ${guessedLetters.length > 0 ? guessedLetters.join(' ') : 'none'}`;\n    const potInfo = `Current pot: ${gameState.pot || 0} currency`;\n\n    return `${hangmanStatus}\\n${wordDisplay}\\n${wrongGuessesText}\\n${guessedLettersText}\\n${potInfo}`;\n}\n\n/**\n * Checks if a player has permission to perform an action\n */\nexport function hasPermission(pog, permission) {\n    for (const role of pog.roles) {\n        const perm = role.permissions.find(p => p.permission === permission);\n        if (perm) return true;\n    }\n    return false;\n}"
        }
      ],
      "permissions": [
        {
          "permission": "HANGMAN_HOST",
          "friendlyName": "Host Hangman Game",
          "description": "Allows the player to start and host Hangman games.",
          "canHaveCount": false
        },
        {
          "permission": "HANGMAN_PLAY",
          "friendlyName": "Play Hangman",
          "description": "Allows the player to join and make guesses in Hangman games.",
          "canHaveCount": false
        },
        {
          "permission": "HANGMAN_ADMIN",
          "friendlyName": "Hangman Admin",
          "description": "Allows the player to manage and control running Hangman games.",
          "canHaveCount": false
        }
      ]
    }
  ]
}