diff --git a/README.md b/README.md index feb17806..d7505d8d 100644 --- a/README.md +++ b/README.md @@ -261,7 +261,7 @@ in the appropriate channel's topic to use it. ## Commands -Total: 578 +Total: 579 ### Utility: @@ -616,6 +616,7 @@ Total: 578 * **gunfight:** Engage in a western gunfight against another user. High noon. * **imposter:** Who is the imposter among us? * **island:** Who will be the final two left on the island after a series of vote-kicks? +* **jenga:** Play a game of Jenga with another user or the AI. * **lie-swatter:** Players are given a fact and must quickly decide if it's True or a Lie. * **pick-a-number:** Two players pick a number between 1 and 10. Whoever's closer wins. * **poker:** Play poker with up to 5 other users. @@ -1337,6 +1338,8 @@ here. * jeopardy (Music, Original Show) * jeopardy-question (Original Show) * soundboard (Jeopardy Sound) +- [Jenga](https://jenga.com/) + * jenga (Original Game) - [Jessica Knable](https://picsart.com/u/jessicaknable) * hearts ([Image](https://picsart.com/i/sticker-hearts-heart-borders-frames-round-frame-border-love-263412201018212)) - [Jisho](https://jisho.org/) diff --git a/commands/games-mp/connect-four.js b/commands/games-mp/connect-four.js index 7b7bab96..6f919b2f 100644 --- a/commands/games-mp/connect-four.js +++ b/commands/games-mp/connect-four.js @@ -102,8 +102,9 @@ module.exports = class ConnectFourCommand extends Command { i = AIEngine.playAI('hard'); lastMove = i + 1; } else { + const emoji = userTurn ? playerOneEmoji : playerTwoEmoji; await msg.say(stripIndents` - ${user}, which column do you pick? Type \`end\` to forefeit. + ${emoji} ${user}, which column do you pick? Type \`end\` to forefeit. Can't think of a move? Use \`play for me\`. ${opponent.bot ? `I placed mine in **${lastMove}**.` : `Previous Move: **${lastMove}**`} @@ -122,7 +123,7 @@ module.exports = class ConnectFourCommand extends Command { max: 1, time: 60000 }); - const choice = turn ? turn.first().content : null; + const choice = turn.size ? turn.first().content : null; if (!choice) { await msg.say('Sorry, time is up! I\'ll pick their move for them.'); i = AIEngine.playAI('hard'); diff --git a/commands/games-mp/jenga.js b/commands/games-mp/jenga.js new file mode 100644 index 00000000..1cf10edb --- /dev/null +++ b/commands/games-mp/jenga.js @@ -0,0 +1,114 @@ +const Command = require('../../structures/Command'); +const { stripIndents } = require('common-tags'); +const { verify } = require('../../util/Util'); +const nums = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟']; + +module.exports = class JengaCommand extends Command { + constructor(client) { + super(client, { + name: 'jenga', + group: 'games-mp', + memberName: 'jenga', + description: 'Play a game of Jenga with another user or the AI.', + credit: [ + { + name: 'Jenga', + url: 'https://jenga.com/', + reason: 'Original Game' + } + ], + args: [ + { + key: 'opponent', + prompt: 'What user would you like to challenge?', + type: 'user', + default: () => this.client.user + } + ] + }); + } + + async run(msg, { opponent }) { + if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); + const current = this.client.games.get(msg.channel.id); + if (current) return msg.reply(`Please wait until the current game of \`${current.name}\` is finished.`); + this.client.games.set(msg.channel.id, { name: this.name }); + try { + if (!opponent.bot) { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) { + this.client.games.delete(msg.channel.id); + return msg.say('Looks like they declined...'); + } + } + const board = [true, true, true, true, true, true, true, true, true, true]; + let userTurn = true; + let winner = null; + let lastTurnTimeout = false; + while (!winner && board.length) { + const user = userTurn ? msg.author : opponent; + let i; + if (opponent.bot && !userTurn) { + i = Math.floor(Math.random() * board.length); + } else { + await msg.say(stripIndents` + ${user}, which block do you want to remove? Type \`end\` to forefeit. + Each block you go lower on the tower, the more likely the tower falls. + + ${this.displayBoard(board)} + `); + const pickFilter = res => { + if (res.author.id !== user.id) return false; + const choice = res.content; + if (choice.toLowerCase() === 'end') return true; + const i = Number.parseInt(choice, 10) - 1; + return board[i]; + }; + const turn = await msg.channel.awaitMessages(pickFilter, { + max: 1, + time: 60000 + }); + if (!turn.size) { + if (lastTurnTimeout) { + await msg.say('Game ended due to inactivity.'); + winner = 'time'; + break; + } else { + await msg.say('Sorry, time is up!'); + lastTurnTimeout = true; + continue; + } + } + const choice = turn.first().content; + const picked = Number.parseInt(choice, 10); + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + i = picked - 1; + } + const numToUse = Math.ceil(((board.length + 1) - ((board.length + 1) - (picked + 1))) / 2); + const notFell = Math.floor(Math.random() * numToUse); + if (!notFell) { + winner = userTurn ? opponent : msg.author; + await msg.say('And the tower topples!'); + break; + } + await msg.say('Thankfully, the tower stands.'); + board.shift(); + userTurn = !userTurn; + } + this.client.games.delete(msg.channel.id); + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(winner ? `Congrats, ${winner}!` : 'Looks like it\'s a draw...'); + } catch (err) { + this.client.games.delete(msg.channel.id); + throw err; + } + } + + displayBoard(board) { + return board.map((b, i) => `${i % 2 ? ' ' : nums[i]}🟫🟫🟫🟫🟫${i % 2 ? nums[i] : ''}`).join('\n'); + } +}; diff --git a/package.json b/package.json index 98e48888..5469a525 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "126.6.8", + "version": "126.7.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": {