From a135cac031be8e929049112a5cb91842c69e9944 Mon Sep 17 00:00:00 2001 From: Dragon Fire Date: Sat, 30 Mar 2024 11:59:42 -0400 Subject: [PATCH] Finish removing "Oh no, an error occurred" --- commands/games-mp/apples-to-apples.js | 16 +- commands/games-mp/balloon-pop.js | 89 +++-- commands/games-mp/battle.js | 137 ++++---- commands/games-mp/bingo.js | 131 ++++---- commands/games-mp/car-race.js | 263 +++++++-------- commands/games-mp/cards-against-humanity.js | 16 +- commands/games-mp/chess.js | 291 ++++++++--------- commands/games-mp/connect-four.js | 185 +++++------ commands/games-mp/cram.js | 169 +++++----- commands/games-mp/domineering.js | 165 +++++----- commands/games-mp/dots-and-boxes.js | 179 +++++----- commands/games-mp/emoji-emoji-revolution.js | 87 +++-- commands/games-mp/guesspionage.js | 207 ++++++------ commands/games-mp/gunfight.js | 69 ++-- commands/games-mp/imposter.js | 253 +++++++-------- commands/games-mp/island.js | 165 +++++----- commands/games-mp/jenga.js | 153 ++++----- commands/games-mp/lie-swatter.js | 156 +++++---- commands/games-mp/mafia.js | 106 +++--- commands/games-mp/nim.js | 179 +++++----- commands/games-mp/obstruction.js | 163 +++++----- commands/games-mp/pick-a-number.js | 95 +++--- commands/games-mp/poker.js | 306 ++++++++---------- commands/games-mp/quiz-duel.js | 156 +++++---- commands/games-mp/russian-roulette.js | 133 ++++---- commands/games-mp/spam-war.js | 57 ++-- commands/games-mp/tic-tac-toe.js | 141 ++++---- commands/games-mp/typing-race.js | 79 ++--- commands/games-mp/word-chain.js | 109 +++---- commands/games-mp/word-spud.js | 87 +++-- commands/games-sp/anagramica.js | 104 +++--- commands/games-sp/anime-score.js | 53 ++- .../games-sp/antidepressant-or-tolkien.js | 1 + commands/games-sp/blackjack.js | 141 ++++---- commands/games-sp/box-choosing.js | 90 +++--- commands/games-sp/captcha.js | 1 + commands/games-sp/doors.js | 34 +- commands/games-sp/google-feud.js | 66 ++-- commands/games-sp/guess-song.js | 45 ++- commands/games-sp/hangman.js | 128 ++++---- commands/games-sp/hearing-test.js | 51 ++- commands/games-sp/horse-race.js | 1 + commands/games-sp/hunger-games.js | 81 ++--- commands/games-sp/jeopardy.js | 56 ++-- commands/games-sp/mad-libs.js | 58 ++-- commands/games-sp/math-quiz.js | 1 + commands/games-sp/memory.js | 46 ++- commands/games-sp/minesweeper.js | 196 ++++++----- commands/games-sp/pokemon-advantage.js | 66 ++-- commands/games-sp/quiz.js | 59 ++-- commands/games-sp/reaction-time.js | 62 ++-- commands/games-sp/sorting-hat.js | 101 +++--- commands/games-sp/tarot.js | 52 ++- commands/games-sp/typing-test.js | 3 +- commands/games-sp/whos-that-pokemon-cry.js | 46 ++- commands/games-sp/whos-that-pokemon.js | 54 ++-- commands/games-sp/would-you-rather.js | 57 ++-- commands/pokedex/pokedex-ability.js | 18 +- commands/pokedex/pokedex-cry.js | 2 +- commands/pokedex/pokedex-image.js | 6 +- commands/pokedex/pokedex-item.js | 22 +- commands/pokedex/pokedex-location.js | 42 ++- commands/pokedex/pokedex-move.js | 30 +- commands/pokedex/pokedex-moveset.js | 34 +- commands/pokedex/pokedex-stats.js | 106 +++--- commands/pokedex/pokedex.js | 112 ++++--- commands/pokedex/smogon.js | 45 ++- commands/random-img/bird.js | 20 +- commands/random-img/bunny.js | 34 +- commands/random-img/cat.js | 14 +- commands/random-img/dog.js | 14 +- commands/random-img/duck.js | 8 +- commands/random-img/fox.js | 8 +- commands/random-img/goose.js | 8 +- commands/random-img/inspiration.js | 12 +- commands/random-img/light-novel-cover.js | 16 +- commands/random-img/lizard.js | 8 +- commands/random-img/lorem-picsum.js | 8 +- commands/random-img/shiba.js | 20 +- commands/random-res/advice.js | 10 +- commands/random-res/boredom.js | 8 +- commands/random-res/chuck-norris.js | 8 +- commands/random-res/fact.js | 42 ++- commands/random-res/fml.js | 12 +- commands/random-res/github-zen.js | 8 +- commands/random-res/joke.js | 22 +- commands/random-res/light-novel-title.js | 8 +- commands/random-res/mtg-card.js | 8 +- commands/random-res/name.js | 24 +- commands/random-res/number-fact.js | 2 +- commands/random-res/pun.js | 22 +- commands/random-res/superpower.js | 16 +- commands/random-seed/friendship.js | 54 ++-- commands/random-seed/ship.js | 54 ++-- commands/random-seed/think-of.js | 56 ++-- commands/search/anilist.js | 14 +- commands/search/anime-character.js | 34 +- commands/search/anime-staff.js | 46 ++- commands/search/anime.js | 52 ++- commands/search/define.js | 24 +- commands/search/frinkiac.js | 44 ++- commands/search/google-autofill.js | 22 +- commands/search/gravatar.js | 2 +- commands/search/http-cat.js | 2 +- commands/search/know-your-meme.js | 26 +- commands/search/manga.js | 52 ++- commands/search/nasa.js | 40 ++- commands/search/neopet.js | 2 +- commands/search/neopets-item.js | 26 +- commands/search/urban.js | 34 +- commands/search/wikipedia.js | 46 ++- commands/search/xkcd.js | 50 ++- commands/search/yu-gi-oh.js | 62 ++-- commands/voice/dec-talk.js | 2 +- commands/voice/mindfulness.js | 2 +- commands/voice/tts.js | 2 +- framework/Client.js | 12 + framework/Command.js | 1 + structures/Client.js | 1 - 119 files changed, 3418 insertions(+), 4116 deletions(-) diff --git a/commands/games-mp/apples-to-apples.js b/commands/games-mp/apples-to-apples.js index 96a602c6..2286379d 100644 --- a/commands/games-mp/apples-to-apples.js +++ b/commands/games-mp/apples-to-apples.js @@ -14,6 +14,7 @@ module.exports = class ApplesToApplesCommand extends Command { memberName: 'apples-to-apples', description: 'Compete to see who can come up with the best card to match an adjective.', guildOnly: true, + game: true, clientPermissions: ['ADD_REACTIONS', 'READ_MESSAGE_HISTORY'], credit: [ { @@ -52,17 +53,10 @@ module.exports = class ApplesToApplesCommand extends Command { async run(msg, { maxPts, flags }) { const bot = flags.bot || flags.b; - 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, - new Game(this.client, this.name, msg.channel, redCards, greenCards, 'Green')); - const game = this.client.games.get(msg.channel.id); + const game = new Game(this.client, this.name, msg.channel, redCards, greenCards, 'Green'); try { const awaitedPlayers = await game.awaitPlayers(msg, bot); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); - } + if (!awaitedPlayers) return msg.say('Game could not be started...'); game.createJoinLeaveCollector(msg.channel, game); while (!game.winner) { const czar = game.changeCzar(); @@ -133,13 +127,11 @@ module.exports = class ApplesToApplesCommand extends Command { } } game.stopJoinLeaveCollector(); - this.client.games.delete(msg.channel.id); if (!game.winner) return msg.say('See you next time!'); return msg.say(`And the winner is... ${game.winner}! Great job!`); } catch (err) { game.stopJoinLeaveCollector(); - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } }; diff --git a/commands/games-mp/balloon-pop.js b/commands/games-mp/balloon-pop.js index 65612cd3..6ee7befe 100644 --- a/commands/games-mp/balloon-pop.js +++ b/commands/games-mp/balloon-pop.js @@ -10,6 +10,7 @@ module.exports = class BalloonPopCommand extends Command { group: 'games-mp', memberName: 'balloon-pop', description: 'Don\'t let yourself be the last one to pump the balloon before it pops!', + game: true, credit: [ { name: 'PAC-MAN Party', @@ -29,64 +30,52 @@ module.exports = class BalloonPopCommand extends Command { } async run(msg, { playersCount }) { - 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 { - const awaitedPlayers = await awaitPlayers(msg, playersCount, 2, this.client.blacklist.user); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); + const awaitedPlayers = await awaitPlayers(msg, playersCount, 2, this.client.blacklist.user); + if (!awaitedPlayers) return msg.say('Game could not be started...'); + const players = new Collection(); + for (const player of awaitedPlayers) { + players.set(player, { + pumps: 0, + id: player, + user: await this.client.users.fetch(player) + }); + } + let loser = null; + let remains = players.size * 250; + let turns = 0; + const rotation = players.map(player => player.id); + while (!loser) { + const user = players.get(rotation[0]); + let pump; + ++turns; + if (turns === 1) { + await msg.say(`${user.user} pumps the balloon!`); + pump = true; + } else { + await msg.say(`${user.user}, do you pump the balloon again?`); + pump = await verify(msg.channel, user.user); } - const players = new Collection(); - for (const player of awaitedPlayers) { - players.set(player, { - pumps: 0, - id: player, - user: await this.client.users.fetch(player) - }); - } - let loser = null; - let remains = players.size * 250; - let turns = 0; - const rotation = players.map(player => player.id); - while (!loser) { - const user = players.get(rotation[0]); - let pump; - ++turns; - if (turns === 1) { - await msg.say(`${user.user} pumps the balloon!`); - pump = true; - } else { - await msg.say(`${user.user}, do you pump the balloon again?`); - pump = await verify(msg.channel, user.user); + if (pump) { + remains -= randomRange(10, 100); + const popped = Math.floor(Math.random() * remains); + if (popped <= 0) { + await msg.say('The balloon pops!'); + loser = user; + break; } - if (pump) { - remains -= randomRange(10, 100); - const popped = Math.floor(Math.random() * remains); - if (popped <= 0) { - await msg.say('The balloon pops!'); - loser = user; - break; - } - if (turns >= 5) { - await msg.say(`${user.user} steps back!`); - turns = 0; - rotation.shift(); - rotation.push(user.id); - } - } else { + if (turns >= 5) { await msg.say(`${user.user} steps back!`); turns = 0; rotation.shift(); rotation.push(user.id); } + } else { + await msg.say(`${user.user} steps back!`); + turns = 0; + rotation.shift(); + rotation.push(user.id); } - this.client.games.delete(msg.channel.id); - return msg.say(`And the loser is... ${loser.user}! Great job everyone else!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; } + return msg.say(`And the loser is... ${loser.user}! Great job everyone else!`); } }; diff --git a/commands/games-mp/battle.js b/commands/games-mp/battle.js index 79ef17a0..d837c0ee 100644 --- a/commands/games-mp/battle.js +++ b/commands/games-mp/battle.js @@ -10,6 +10,7 @@ module.exports = class BattleCommand extends Command { group: 'games-mp', memberName: 'battle', description: 'Engage in a turn-based battle against another user or the AI.', + game: true, args: [ { key: 'opponent', @@ -22,80 +23,68 @@ module.exports = class BattleCommand extends Command { async run(msg, { opponent }) { if (opponent.id === msg.author.id) return msg.reply('You may not battle yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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, data: new Battle(msg.author, opponent) }); - const battle = this.client.games.get(msg.channel.id).data; - 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...'); - } - } - while (!battle.winner) { - const choice = await battle.attacker.chooseAction(msg); - if (choice === 'attack') { - const damage = randomRange(battle.defender.guard ? 5 : 20, battle.defender.guard ? 20 : 50); - await msg.say(`${battle.attacker} deals **${damage}** damage!`); - battle.defender.dealDamage(damage); - battle.reset(); - } else if (choice === 'defend') { - await msg.say(`${battle.attacker} defends!`); - battle.attacker.changeGuard(); - battle.reset(false); - } else if (choice === 'special') { - const miss = Math.floor(Math.random() * 5); - if (miss === 0 || miss === 3) { - await msg.say(`${battle.attacker}'s special attack missed!`); - } else if (miss === 1 || miss === 5) { - const damage = randomRange(battle.defender.guard ? 10 : 40, battle.defender.guard ? 40 : 100); - await msg.say(`${battle.attacker}'s special attack grazed the opponent, dealing **${damage}** damage!`); - battle.defender.dealDamage(damage); - } else if (miss === 2) { - const damage = randomRange(battle.defender.guard ? 20 : 80, battle.defender.guard ? 80 : 200); - await msg.say(`${battle.attacker}'s special attack hit directly, dealing **${damage}** damage!`); - battle.defender.dealDamage(damage); - } - battle.attacker.useMP(25); - battle.reset(); - } else if (choice === 'cure') { - const amount = Math.round(battle.attacker.mp / 2); - await msg.say(`${battle.attacker} heals **${amount}** HP!`); - battle.attacker.heal(amount); - battle.attacker.useMP(battle.attacker.mp); - battle.reset(); - } else if (choice === 'final') { - await msg.say(`${battle.attacker} uses their final move, dealing **100** damage!`); - battle.defender.dealDamage(100); - battle.attacker.useMP(50); - battle.attacker.usedFinal = true; - battle.reset(); - } else if (choice === 'run') { - await msg.say(`${battle.attacker} flees!`); - battle.attacker.forfeit(); - } else if (choice === 'failed:time') { - await msg.say(`Time's up, ${battle.attacker}!`); - battle.reset(); - if (battle.lastTurnTimeout) { - battle.endedDueToInactivity = true; - break; - } else { - battle.lastTurnTimeout = true; - } - } else { - await msg.say('I do not understand what you want to do.'); - } - if (choice !== 'failed:time' && battle.lastTurnTimeout) battle.lastTurnTimeout = false; - } - this.client.games.delete(msg.channel.id); - if (battle.winner === 'time') return msg.say('Game ended due to inactivity.'); - return msg.say(`The match is over! Congrats, ${battle.winner}!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const battle = new Battle(msg.author, opponent); + if (!opponent.bot) { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); } + while (!battle.winner) { + const choice = await battle.attacker.chooseAction(msg); + if (choice === 'attack') { + const damage = randomRange(battle.defender.guard ? 5 : 20, battle.defender.guard ? 20 : 50); + await msg.say(`${battle.attacker} deals **${damage}** damage!`); + battle.defender.dealDamage(damage); + battle.reset(); + } else if (choice === 'defend') { + await msg.say(`${battle.attacker} defends!`); + battle.attacker.changeGuard(); + battle.reset(false); + } else if (choice === 'special') { + const miss = Math.floor(Math.random() * 5); + if (miss === 0 || miss === 3) { + await msg.say(`${battle.attacker}'s special attack missed!`); + } else if (miss === 1 || miss === 5) { + const damage = randomRange(battle.defender.guard ? 10 : 40, battle.defender.guard ? 40 : 100); + await msg.say(`${battle.attacker}'s special attack grazed the opponent, dealing **${damage}** damage!`); + battle.defender.dealDamage(damage); + } else if (miss === 2) { + const damage = randomRange(battle.defender.guard ? 20 : 80, battle.defender.guard ? 80 : 200); + await msg.say(`${battle.attacker}'s special attack hit directly, dealing **${damage}** damage!`); + battle.defender.dealDamage(damage); + } + battle.attacker.useMP(25); + battle.reset(); + } else if (choice === 'cure') { + const amount = Math.round(battle.attacker.mp / 2); + await msg.say(`${battle.attacker} heals **${amount}** HP!`); + battle.attacker.heal(amount); + battle.attacker.useMP(battle.attacker.mp); + battle.reset(); + } else if (choice === 'final') { + await msg.say(`${battle.attacker} uses their final move, dealing **100** damage!`); + battle.defender.dealDamage(100); + battle.attacker.useMP(50); + battle.attacker.usedFinal = true; + battle.reset(); + } else if (choice === 'run') { + await msg.say(`${battle.attacker} flees!`); + battle.attacker.forfeit(); + } else if (choice === 'failed:time') { + await msg.say(`Time's up, ${battle.attacker}!`); + battle.reset(); + if (battle.lastTurnTimeout) { + battle.endedDueToInactivity = true; + break; + } else { + battle.lastTurnTimeout = true; + } + } else { + await msg.say('I do not understand what you want to do.'); + } + if (choice !== 'failed:time' && battle.lastTurnTimeout) battle.lastTurnTimeout = false; + } + if (battle.winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(`The match is over! Congrats, ${battle.winner}!`); } }; diff --git a/commands/games-mp/bingo.js b/commands/games-mp/bingo.js index 72fc1f41..fe1f0c76 100644 --- a/commands/games-mp/bingo.js +++ b/commands/games-mp/bingo.js @@ -15,6 +15,7 @@ module.exports = class BingoCommand extends Command { memberName: 'bingo', description: 'Play bingo with up to 99 other users.', guildOnly: true, + game: true, args: [ { key: 'playersCount', @@ -27,78 +28,66 @@ module.exports = class BingoCommand extends Command { } async run(msg, { playersCount }) { - 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 { - const awaitedPlayers = await awaitPlayers(msg, playersCount, 1, this.client.blacklist.user); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); - } - const players = new Collection(); - for (const player of awaitedPlayers) { - players.set(player, { - board: this.generateBoard(), - id: player, - user: await this.client.users.fetch(player) - }); - } - let winner = null; - const called = ['FR']; - while (!winner) { - const validNums = callNums.filter(num => !called.includes(num)); - if (!validNums.length) break; - const picked = validNums[Math.floor(Math.random() * validNums.length)]; - called.push(picked); - for (const player of players.values()) { - try { - await player.user.send(stripIndents` - **${this.findRowValue(picked)} ${picked}** was called in ${msg.channel}. - ${this.generateBoardDisplay(player.board, called)} - `); - } catch { - await msg.say(`${player.user}, I couldn't send your board! Turn on DMs!`); - } - } - await msg.say(stripIndents` - **${this.findRowValue(picked)} ${picked}**! - - Check your DMs for your board. If you have bingo, type \`bingo\`! - If you wish to drop out, type \`leave game\`. - _Next number will be called in 20 seconds. ${validNums.length - 1} numbers left._ - `); - const filter = res => { - if (!players.has(res.author.id)) return false; - if (res.content.toLowerCase() === 'leave game') { - players.delete(res.author.id); - reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); - if (!players.size) return true; - return false; - } - if (res.content.toLowerCase() !== 'bingo') return false; - if (!this.checkBingo(players.get(res.author.id).board, called)) { - msg.say(`${res.author}, you don't have bingo, liar.`).catch(() => null); - return false; - } - return true; - }; - const bingo = await msg.channel.awaitMessages({ filter, max: 1, time: 20000 }); - if (!players.size) { - winner = 0; - break; - } - if (!bingo.size) continue; - winner = bingo.first().author; - } - this.client.games.delete(msg.channel.id); - if (winner === 0) return msg.say('Everyone dropped out...'); - if (!winner) return msg.say('I called the entire board, but no one called bingo...'); - return msg.say(`Congrats, ${winner}!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const awaitedPlayers = await awaitPlayers(msg, playersCount, 1, this.client.blacklist.user); + if (!awaitedPlayers) return msg.say('Game could not be started...'); + const players = new Collection(); + for (const player of awaitedPlayers) { + players.set(player, { + board: this.generateBoard(), + id: player, + user: await this.client.users.fetch(player) + }); } + let winner = null; + const called = ['FR']; + while (!winner) { + const validNums = callNums.filter(num => !called.includes(num)); + if (!validNums.length) break; + const picked = validNums[Math.floor(Math.random() * validNums.length)]; + called.push(picked); + for (const player of players.values()) { + try { + await player.user.send(stripIndents` + **${this.findRowValue(picked)} ${picked}** was called in ${msg.channel}. + ${this.generateBoardDisplay(player.board, called)} + `); + } catch { + await msg.say(`${player.user}, I couldn't send your board! Turn on DMs!`); + } + } + await msg.say(stripIndents` + **${this.findRowValue(picked)} ${picked}**! + + Check your DMs for your board. If you have bingo, type \`bingo\`! + If you wish to drop out, type \`leave game\`. + _Next number will be called in 20 seconds. ${validNums.length - 1} numbers left._ + `); + const filter = res => { + if (!players.has(res.author.id)) return false; + if (res.content.toLowerCase() === 'leave game') { + players.delete(res.author.id); + reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); + if (!players.size) return true; + return false; + } + if (res.content.toLowerCase() !== 'bingo') return false; + if (!this.checkBingo(players.get(res.author.id).board, called)) { + msg.say(`${res.author}, you don't have bingo, liar.`).catch(() => null); + return false; + } + return true; + }; + const bingo = await msg.channel.awaitMessages({ filter, max: 1, time: 20000 }); + if (!players.size) { + winner = 0; + break; + } + if (!bingo.size) continue; + winner = bingo.first().author; + } + if (winner === 0) return msg.say('Everyone dropped out...'); + if (!winner) return msg.say('I called the entire board, but no one said bingo...'); + return msg.say(`Congrats, ${winner}!`); } generateBoard() { diff --git a/commands/games-mp/car-race.js b/commands/games-mp/car-race.js index 6e1cc30d..315beb1b 100644 --- a/commands/games-mp/car-race.js +++ b/commands/games-mp/car-race.js @@ -25,6 +25,7 @@ module.exports = class CarRaceCommand extends Command { group: 'games-mp', memberName: 'car-race', description: 'Race a car against another user or the AI.', + game: true, credit: [ { name: 'iStock', @@ -352,9 +353,6 @@ module.exports = class CarRaceCommand extends Command { async run(msg, { opponent, car }) { if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 }); const bg = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'car-race', 'bg.png')); const userData = { user: msg.author, @@ -370,147 +368,134 @@ module.exports = class CarRaceCommand extends Command { const userAvatar = await request.get(msg.author.displayAvatarURL({ format: 'png', size: 128 })); userData.avatar = await loadImage(userAvatar.body); let difficulty; - try { - const available = cars.filter(car2 => car !== car2); - if (opponent.bot) { - await msg.reply(`What difficulty do you want to use? Either ${list(Object.keys(difficulties), 'or')}.`); - const difficultyFilter = res => { - if (res.author.id !== msg.author.id) return false; - return Object.keys(difficulties).includes(res.content.toLowerCase()); - }; - const difficultyPick = await msg.channel.awaitMessages({ - filter: difficultyFilter, - max: 1, - time: 30000 - }); - if (!difficultyPick.size) { - this.client.games.delete(msg.channel.id); - return msg.say('Failed to pick difficulty. Aborted command.'); - } - difficulty = difficultyPick.first().content.toLowerCase(); - const oppoCarPick = available[Math.floor(Math.random() * available.length)]; + const available = cars.filter(car2 => car !== car2); + if (opponent.bot) { + await msg.reply(`What difficulty do you want to use? Either ${list(Object.keys(difficulties), 'or')}.`); + const difficultyFilter = res => { + if (res.author.id !== msg.author.id) return false; + return Object.keys(difficulties).includes(res.content.toLowerCase()); + }; + const difficultyPick = await msg.channel.awaitMessages({ + filter: difficultyFilter, + max: 1, + time: 30000 + }); + if (!difficultyPick.size) return msg.say('Failed to pick difficulty. Aborted command.'); + difficulty = difficultyPick.first().content.toLowerCase(); + const oppoCarPick = available[Math.floor(Math.random() * available.length)]; + oppoData.car = await loadImage( + path.join(__dirname, '..', '..', 'assets', 'images', 'car-race', 'cars', `${oppoCarPick}.png`) + ); + } else { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + await msg.say(`${opponent}, what car do you want to be? Either ${list(available, 'or')}.`); + const filter = res => { + if (res.author.id !== opponent.id) return false; + return available.includes(res.content.toLowerCase()); + }; + const p2Car = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (p2Car.size) { + const choice = p2Car.first().content.toLowerCase(); oppoData.car = await loadImage( - path.join(__dirname, '..', '..', 'assets', 'images', 'car-race', 'cars', `${oppoCarPick}.png`) + path.join(__dirname, '..', '..', 'assets', 'images', 'car-race', 'cars', `${choice}.png`) ); } else { - 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...'); - } - await msg.say(`${opponent}, what car do you want to be? Either ${list(available, 'or')}.`); - const filter = res => { - if (res.author.id !== opponent.id) return false; - return available.includes(res.content.toLowerCase()); - }; - const p2Car = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (p2Car.size) { - const choice = p2Car.first().content.toLowerCase(); - oppoData.car = await loadImage( - path.join(__dirname, '..', '..', 'assets', 'images', 'car-race', 'cars', `${choice}.png`) - ); - } else { - const chosen = cars[Math.floor(Math.random() * cars.length)]; - oppoData.car = await loadImage( - path.join(__dirname, '..', '..', 'assets', 'images', 'car-race', 'cars', `${chosen}.png`) - ); - } + const chosen = cars[Math.floor(Math.random() * cars.length)]; + oppoData.car = await loadImage( + path.join(__dirname, '..', '..', 'assets', 'images', 'car-race', 'cars', `${chosen}.png`) + ); } - const oppoAvatar = await request.get(opponent.displayAvatarURL({ format: 'png', size: 128 })); - oppoData.avatar = await loadImage(oppoAvatar.body); - let lastRoundWinner; - let lastTurnTimeout = false; - while (userData.spaces < 7 && oppoData.spaces < 7) { - const board = await this.generateBoard(bg, userData, oppoData, lastRoundWinner); - let text; - if (lastRoundWinner) { - if (userData.spaces > oppoData.spaces || oppoData.spaces > userData.spaces) { - const leader = userData.spaces > oppoData.spaces ? msg.author : opponent; - if (leader.id === lastRoundWinner.id) text = `${lastRoundWinner} pulls ahead!`; - else text = `${lastRoundWinner} catches up!`; - } else if (userData.spaces === oppoData.spaces) { - text = `${lastRoundWinner} ties it up!`; - } - } else { - text = stripIndents` - Welcome to \`car-race\`! Whenever a message pops up, type the word provided. - Whoever types the word first advances their car! - Either player can type \`end\` at any time to end the game. - `; - } - await msg.say(`${text}\nGet Ready...`, { files: [{ attachment: board, name: 'car-race.png' }] }); - const earlyFilter = res => { - if (![opponent.id, msg.author.id].includes(res.author.id)) return false; - return res.content.toLowerCase() === 'end'; - }; - const earlyEnd = await msg.channel.awaitMessages({ - filter: earlyFilter, - max: 1, - time: randomRange(1000, 30000) - }); - if (earlyEnd.size) { - if (earlyEnd.first().author.id === msg.author.id) oppoData.spaces = 7; - else if (earlyEnd.first().author.id === opponent.id) userData.spaces = 7; - break; - } - const word = words[Math.floor(Math.random() * words.length)]; - await msg.say(`TYPE \`${word.toUpperCase()}\` NOW!`); - const turnFilter = res => { - if (![opponent.id, msg.author.id].includes(res.author.id)) return false; - if (res.content.toLowerCase() === 'end') return true; - return res.content.toLowerCase() === word; - }; - const winner = await msg.channel.awaitMessages({ - filter: turnFilter, - max: 1, - time: opponent.bot ? difficulties[difficulty] : 30000 - }); - if (!winner.size) { - if (opponent.bot) { - oppoData.spaces += 1; - lastRoundWinner = opponent; - if (lastTurnTimeout) lastTurnTimeout = false; - continue; - } else if (lastTurnTimeout) { - this.client.games.delete(msg.channel.id); - return msg.say('Game ended due to inactivity.'); - } else { - await msg.say('Come on, get your head in the game!'); - lastTurnTimeout = true; - continue; - } - } - const win = winner.first(); - if (win.content.toLowerCase() === 'end') { - if (win.author.id === msg.author.id) { - oppoData.spaces = 7; - lastRoundWinner = opponent; - } else if (win.author.id === opponent.id) { - userData.spaces = 7; - lastRoundWinner = msg.author; - } - break; - } - if (win.author.id === msg.author.id) userData.spaces += 1; - else if (win.author.id === opponent.id) oppoData.spaces += 1; - lastRoundWinner = win.author; - if (lastTurnTimeout) lastTurnTimeout = false; - } - this.client.games.delete(msg.channel.id); - const winner = userData.spaces > oppoData.spaces ? msg.author : opponent; - const board = await this.generateBoard(bg, userData, oppoData, lastRoundWinner, winner); - return msg.say(`Congrats, ${winner}!`, { - files: [{ attachment: board, name: 'car-race-win.png' }] - }); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; } + const oppoAvatar = await request.get(opponent.displayAvatarURL({ format: 'png', size: 128 })); + oppoData.avatar = await loadImage(oppoAvatar.body); + let lastRoundWinner; + let lastTurnTimeout = false; + while (userData.spaces < 7 && oppoData.spaces < 7) { + const board = await this.generateBoard(bg, userData, oppoData, lastRoundWinner); + let text; + if (lastRoundWinner) { + if (userData.spaces > oppoData.spaces || oppoData.spaces > userData.spaces) { + const leader = userData.spaces > oppoData.spaces ? msg.author : opponent; + if (leader.id === lastRoundWinner.id) text = `${lastRoundWinner} pulls ahead!`; + else text = `${lastRoundWinner} catches up!`; + } else if (userData.spaces === oppoData.spaces) { + text = `${lastRoundWinner} ties it up!`; + } + } else { + text = stripIndents` + Welcome to \`car-race\`! Whenever a message pops up, type the word provided. + Whoever types the word first advances their car! + Either player can type \`end\` at any time to end the game. + `; + } + await msg.say(`${text}\nGet Ready...`, { files: [{ attachment: board, name: 'car-race.png' }] }); + const earlyFilter = res => { + if (![opponent.id, msg.author.id].includes(res.author.id)) return false; + return res.content.toLowerCase() === 'end'; + }; + const earlyEnd = await msg.channel.awaitMessages({ + filter: earlyFilter, + max: 1, + time: randomRange(1000, 30000) + }); + if (earlyEnd.size) { + if (earlyEnd.first().author.id === msg.author.id) oppoData.spaces = 7; + else if (earlyEnd.first().author.id === opponent.id) userData.spaces = 7; + break; + } + const word = words[Math.floor(Math.random() * words.length)]; + await msg.say(`TYPE \`${word.toUpperCase()}\` NOW!`); + const turnFilter = res => { + if (![opponent.id, msg.author.id].includes(res.author.id)) return false; + if (res.content.toLowerCase() === 'end') return true; + return res.content.toLowerCase() === word; + }; + const winner = await msg.channel.awaitMessages({ + filter: turnFilter, + max: 1, + time: opponent.bot ? difficulties[difficulty] : 30000 + }); + if (!winner.size) { + if (opponent.bot) { + oppoData.spaces += 1; + lastRoundWinner = opponent; + if (lastTurnTimeout) lastTurnTimeout = false; + continue; + } else if (lastTurnTimeout) { + return msg.say('Game ended due to inactivity.'); + } else { + await msg.say('Come on, get your head in the game!'); + lastTurnTimeout = true; + continue; + } + } + const win = winner.first(); + if (win.content.toLowerCase() === 'end') { + if (win.author.id === msg.author.id) { + oppoData.spaces = 7; + lastRoundWinner = opponent; + } else if (win.author.id === opponent.id) { + userData.spaces = 7; + lastRoundWinner = msg.author; + } + break; + } + if (win.author.id === msg.author.id) userData.spaces += 1; + else if (win.author.id === opponent.id) oppoData.spaces += 1; + lastRoundWinner = win.author; + if (lastTurnTimeout) lastTurnTimeout = false; + } + const winner = userData.spaces > oppoData.spaces ? msg.author : opponent; + const board = await this.generateBoard(bg, userData, oppoData, lastRoundWinner, winner); + return msg.say(`Congrats, ${winner}!`, { + files: [{ attachment: board, name: 'car-race-win.png' }] + }); } async generateBoard(bg, userData, oppoData, turnWin, win) { diff --git a/commands/games-mp/cards-against-humanity.js b/commands/games-mp/cards-against-humanity.js index a22d5d9f..0dfdeb71 100644 --- a/commands/games-mp/cards-against-humanity.js +++ b/commands/games-mp/cards-against-humanity.js @@ -15,6 +15,7 @@ module.exports = class CardsAgainstHumanityCommand extends Command { description: 'Compete to see who can come up with the best card to fill in the blank.', guildOnly: true, nsfw: true, + game: true, clientPermissions: ['ADD_REACTIONS', 'READ_MESSAGE_HISTORY'], credit: [ { @@ -52,17 +53,10 @@ module.exports = class CardsAgainstHumanityCommand extends Command { async run(msg, { maxPts, flags }) { const bot = flags.bot || flags.b; - 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, - new Game(this.client, this.name, msg.channel, whiteCards, blackCards, 'Black')); - const game = this.client.games.get(msg.channel.id); + const game = new Game(this.client, this.name, msg.channel, whiteCards, blackCards, 'Black'); try { const awaitedPlayers = await game.awaitPlayers(msg, bot); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); - } + if (!awaitedPlayers) return msg.say('Game could not be started...'); game.createJoinLeaveCollector(msg.channel, game); while (!game.winner) { const czar = game.changeCzar(); @@ -133,13 +127,11 @@ module.exports = class CardsAgainstHumanityCommand extends Command { } } game.stopJoinLeaveCollector(); - this.client.games.delete(msg.channel.id); if (!game.winner) return msg.say('See you next time!'); return msg.say(`And the winner is... ${game.winner}! Great job!`); } catch (err) { game.stopJoinLeaveCollector(); - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } }; diff --git a/commands/games-mp/chess.js b/commands/games-mp/chess.js index 4e087a1e..76b9ca4b 100644 --- a/commands/games-mp/chess.js +++ b/commands/games-mp/chess.js @@ -19,6 +19,7 @@ module.exports = class ChessCommand extends Command { group: 'games-mp', memberName: 'chess', description: 'Play a game of Chess with another user or the AI.', + game: true, credit: [ { name: 'PNGkey.com', @@ -58,169 +59,155 @@ module.exports = class ChessCommand extends Command { async run(msg, { opponent, time, fen }) { if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 }); if (!this.images) await this.loadImages(); - 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 resumeGame = await this.client.redis.get(`chess-${msg.author.id}`); - let game; - let whiteTime = time === 0 ? Infinity : time * 60000; - let blackTime = time === 0 ? Infinity : time * 60000; - let whitePlayer = msg.author; - let blackPlayer = opponent; - if (resumeGame) { - await msg.reply(stripIndents` - You have a saved game, do you want to resume it? - **This will delete your saved game.** - `); - const verification = await verify(msg.channel, msg.author); - if (verification) { - try { - const data = JSON.parse(resumeGame); - game = new jsChess.Game(data.fen); - whiteTime = data.whiteTime === -1 ? Infinity : data.whiteTime; - blackTime = data.blackTime === -1 ? Infinity : data.blackTime; - whitePlayer = data.color === 'white' ? msg.author : opponent; - blackPlayer = data.color === 'black' ? msg.author : opponent; - await this.client.redis.del(`chess-${msg.author.id}`); - } catch { - this.client.games.delete(msg.channel.id); - await this.client.redis.del(`chess-${msg.author.id}`); - return msg.reply('An error occurred reading your saved game. Please try again.'); - } - } else { - game = new jsChess.Game(fen || undefined); + if (!opponent.bot) { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + } + const resumeGame = await this.client.redis.get(`chess-${msg.author.id}`); + let game; + let whiteTime = time === 0 ? Infinity : time * 60000; + let blackTime = time === 0 ? Infinity : time * 60000; + let whitePlayer = msg.author; + let blackPlayer = opponent; + if (resumeGame) { + await msg.reply(stripIndents` + You have a saved game, do you want to resume it? + **This will delete your saved game.** + `); + const verification = await verify(msg.channel, msg.author); + if (verification) { + try { + const data = JSON.parse(resumeGame); + game = new jsChess.Game(data.fen); + whiteTime = data.whiteTime === -1 ? Infinity : data.whiteTime; + blackTime = data.blackTime === -1 ? Infinity : data.blackTime; + whitePlayer = data.color === 'white' ? msg.author : opponent; + blackPlayer = data.color === 'black' ? msg.author : opponent; + await this.client.redis.del(`chess-${msg.author.id}`); + } catch { + await this.client.redis.del(`chess-${msg.author.id}`); + return msg.reply('An error occurred reading your saved game. It will be deleted.'); } } else { game = new jsChess.Game(fen || undefined); } - let prevPieces = null; - let saved = false; - while (!game.exportJson().isFinished && game.exportJson().halfMove <= 50) { - const gameState = game.exportJson(); - const user = gameState.turn === 'black' ? blackPlayer : whitePlayer; - const userTime = gameState.turn === 'black' ? blackTime : whiteTime; - if (user.bot) { - prevPieces = Object.assign({}, game.exportJson().pieces); - const now = new Date(); - game.aiMove(1); - const timeTaken = new Date() - now; - if (gameState.turn === 'black') blackTime -= timeTaken - 5000; - if (gameState.turn === 'white') whiteTime -= timeTaken - 5000; - } else { - const displayTime = userTime === Infinity ? 'Infinite' : moment.duration(userTime).format(); - await msg.say(stripIndents` - ${user}, what move do you want to make (ex. A1A2 or NC3)? Type \`end\` to forfeit. - You can save your game by typing \`save\`. Can't think of a move? Use \`play for me\`. - - _You are ${gameState.check ? '**in check!**' : 'not in check.'}_ - **Time Remaining: ${displayTime}** (Max 10min per turn) - **FEN:** \`${game.exportFEN()}\` - `, { files: [{ attachment: this.displayBoard(gameState, prevPieces), name: 'chess.png' }] }); - prevPieces = Object.assign({}, game.exportJson().pieces); - const moves = game.moves(); - const pickFilter = res => { - if (![msg.author.id, opponent.id].includes(res.author.id)) return false; - const choice = res.content.toUpperCase(); - if (choice === 'END') return true; - if (choice === 'SAVE') return true; - if (choice === 'PLAY FOR ME') return true; - if (res.author.id !== user.id) return false; - const move = choice.match(turnRegex); - if (!move) return false; - const parsed = this.parseSAN(gameState, moves, move); - if (!parsed || !moves[parsed[0]] || !moves[parsed[0]].includes(parsed[1])) { - reactIfAble(res, res.author, FAILURE_EMOJI_ID, '❌'); - return false; - } - return true; - }; - const now = new Date(); - const turn = await msg.channel.awaitMessages({ - filter: pickFilter, - max: 1, - time: Math.min(userTime, 600000) - }); - if (!turn.size) { - const timeTaken = new Date() - now; - this.client.games.delete(msg.channel.id); - if (userTime - timeTaken <= 0) { - return msg.say(`${user.id === msg.author.id ? opponent : msg.author} wins from timeout!`); - } else { - return msg.say(`${user}, the game has been ended. You cannot take more than 10 minutes.`); - } - } - if (turn.first().content.toLowerCase() === 'end') break; - if (turn.first().content.toLowerCase() === 'save') { - const { author } = turn.first(); - const alreadySaved = await this.client.redis.get(`chess-${author.id}`); - if (alreadySaved) { - await msg.say('You already have a saved game, do you want to overwrite it?'); - const verification = await verify(msg.channel, author); - if (!verification) continue; // eslint-disable-line max-depth - } - if (gameState.turn === 'black') blackTime -= new Date() - now; - if (gameState.turn === 'white') whiteTime -= new Date() - now; - await this.client.redis.set( - `chess-${author.id}`, - this.exportGame( - game, - blackTime, - whiteTime, - whitePlayer.id === author.id ? 'white' : 'black' - ) - ); - saved = true; - break; - } - if (turn.first().content.toLowerCase() === 'play for me') { - game.aiMove(0); - } else { - const choice = this.parseSAN(gameState, moves, turn.first().content.toUpperCase().match(turnRegex)); - const pawnMoved = gameState.pieces[choice[0]].toUpperCase() === 'P'; - game.move(choice[0], choice[1]); - if (pawnMoved && choice[1].endsWith(gameState.turn === 'white' ? '8' : '1')) { - game.setPiece(choice[1], gameState.turn === 'white' ? choice[2] : choice[2].toLowerCase()); - } - } - const timeTaken = new Date() - now; - if (gameState.turn === 'black') blackTime -= timeTaken - 5000; - if (gameState.turn === 'white') whiteTime -= timeTaken - 5000; - } - } - this.client.games.delete(msg.channel.id); - if (saved) { - return msg.say(stripIndents` - Game saved! Use ${this.usage(opponent.tag)} to resume it. - You do not have to use the same opponent to resume the game. - If you want to delete your saved game, use ${this.client.registry.commands.get('chess-delete').usage()}. - `); - } + } else { + game = new jsChess.Game(fen || undefined); + } + let prevPieces = null; + let saved = false; + while (!game.exportJson().isFinished && game.exportJson().halfMove <= 50) { const gameState = game.exportJson(); - if (gameState.halfMove > 50) return msg.say('Due to the fifty move rule, this game is a draw.'); - if (!gameState.isFinished) return msg.say('Game ended due to forfeit.'); - if (!gameState.checkMate && gameState.isFinished) { - return msg.say('Stalemate! This game is a draw.', { - files: [{ attachment: this.displayBoard(gameState, prevPieces), name: 'chess.png' }] + const user = gameState.turn === 'black' ? blackPlayer : whitePlayer; + const userTime = gameState.turn === 'black' ? blackTime : whiteTime; + if (user.bot) { + prevPieces = Object.assign({}, game.exportJson().pieces); + const now = new Date(); + game.aiMove(1); + const timeTaken = new Date() - now; + if (gameState.turn === 'black') blackTime -= timeTaken - 5000; + if (gameState.turn === 'white') whiteTime -= timeTaken - 5000; + } else { + const displayTime = userTime === Infinity ? 'Infinite' : moment.duration(userTime).format(); + await msg.say(stripIndents` + ${user}, what move do you want to make (ex. A1A2 or NC3)? Type \`end\` to forfeit. + You can save your game by typing \`save\`. Can't think of a move? Use \`play for me\`. + + _You are ${gameState.check ? '**in check!**' : 'not in check.'}_ + **Time Remaining: ${displayTime}** (Max 10min per turn) + **FEN:** \`${game.exportFEN()}\` + `, { files: [{ attachment: this.displayBoard(gameState, prevPieces), name: 'chess.png' }] }); + prevPieces = Object.assign({}, game.exportJson().pieces); + const moves = game.moves(); + const pickFilter = res => { + if (![msg.author.id, opponent.id].includes(res.author.id)) return false; + const choice = res.content.toUpperCase(); + if (choice === 'END') return true; + if (choice === 'SAVE') return true; + if (choice === 'PLAY FOR ME') return true; + if (res.author.id !== user.id) return false; + const move = choice.match(turnRegex); + if (!move) return false; + const parsed = this.parseSAN(gameState, moves, move); + if (!parsed || !moves[parsed[0]] || !moves[parsed[0]].includes(parsed[1])) { + reactIfAble(res, res.author, FAILURE_EMOJI_ID, '❌'); + return false; + } + return true; + }; + const now = new Date(); + const turn = await msg.channel.awaitMessages({ + filter: pickFilter, + max: 1, + time: Math.min(userTime, 600000) }); + if (!turn.size) { + const timeTaken = new Date() - now; + if (userTime - timeTaken <= 0) { + return msg.say(`${user.id === msg.author.id ? opponent : msg.author} wins from timeout!`); + } else { + return msg.say(`${user}, the game has been ended. You cannot take more than 10 minutes.`); + } + } + if (turn.first().content.toLowerCase() === 'end') break; + if (turn.first().content.toLowerCase() === 'save') { + const { author } = turn.first(); + const alreadySaved = await this.client.redis.get(`chess-${author.id}`); + if (alreadySaved) { + await msg.say('You already have a saved game, do you want to overwrite it?'); + const verification = await verify(msg.channel, author); + if (!verification) continue; // eslint-disable-line max-depth + } + if (gameState.turn === 'black') blackTime -= new Date() - now; + if (gameState.turn === 'white') whiteTime -= new Date() - now; + await this.client.redis.set( + `chess-${author.id}`, + this.exportGame( + game, + blackTime, + whiteTime, + whitePlayer.id === author.id ? 'white' : 'black' + ) + ); + saved = true; + break; + } + if (turn.first().content.toLowerCase() === 'play for me') { + game.aiMove(0); + } else { + const choice = this.parseSAN(gameState, moves, turn.first().content.toUpperCase().match(turnRegex)); + const pawnMoved = gameState.pieces[choice[0]].toUpperCase() === 'P'; + game.move(choice[0], choice[1]); + if (pawnMoved && choice[1].endsWith(gameState.turn === 'white' ? '8' : '1')) { + game.setPiece(choice[1], gameState.turn === 'white' ? choice[2] : choice[2].toLowerCase()); + } + } + const timeTaken = new Date() - now; + if (gameState.turn === 'black') blackTime -= timeTaken - 5000; + if (gameState.turn === 'white') whiteTime -= timeTaken - 5000; } - const winner = gameState.turn === 'black' ? whitePlayer : blackPlayer; - return msg.say(`Checkmate! Congrats, ${winner}!`, { + } + if (saved) { + return msg.say(stripIndents` + Game saved! Use ${this.usage(opponent.tag)} to resume it. + You do not have to use the same opponent to resume the game. + If you want to delete your saved game, use ${this.client.registry.commands.get('chess-delete').usage()}. + `); + } + const gameState = game.exportJson(); + if (gameState.halfMove > 50) return msg.say('Due to the fifty move rule, this game is a draw.'); + if (!gameState.isFinished) return msg.say('Game ended due to forfeit.'); + if (!gameState.checkMate && gameState.isFinished) { + return msg.say('Stalemate! This game is a draw.', { files: [{ attachment: this.displayBoard(gameState, prevPieces), name: 'chess.png' }] }); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; } + const winner = gameState.turn === 'black' ? whitePlayer : blackPlayer; + return msg.say(`Checkmate! Congrats, ${winner}!`, { + files: [{ attachment: this.displayBoard(gameState, prevPieces), name: 'chess.png' }] + }); } parseSAN(gameState, moves, move) { diff --git a/commands/games-mp/connect-four.js b/commands/games-mp/connect-four.js index a58bc086..fa96ea67 100644 --- a/commands/games-mp/connect-four.js +++ b/commands/games-mp/connect-four.js @@ -18,6 +18,7 @@ module.exports = class ConnectFourCommand extends Command { group: 'games-mp', memberName: 'connect-four', description: 'Play a game of Connect Four with another user or the AI.', + game: true, credit: [ { name: 'Hasbro', @@ -59,119 +60,107 @@ module.exports = class ConnectFourCommand extends Command { async run(msg, { opponent, color }) { if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 }); const playerOneEmoji = color; let playerTwoEmoji = color === colors.yellow ? colors.red : colors.yellow; - try { - const available = Object.keys(colors).filter(clr => color !== colors[clr]); - if (opponent.bot) { - playerTwoEmoji = colors[available[Math.floor(Math.random() * available.length)]]; - } else { - 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...'); - } - await msg.say( - `${opponent}, what color do you want to be? Either an emoji or one of ${list(available, 'or')}.` - ); - const filter = res => { - if (res.author.id !== opponent.id) return false; - if (res.content === blankEmoji) return false; - const hasEmoji = new RegExp(`^(?:${emojiRegex().source})$`).test(res.content); - const hasCustom = res.content.match(customEmojiRegex); - if (hasCustom && msg.guild && !msg.guild.emojis.cache.has(hasCustom[2])) return false; - return (hasCustom && msg.guild) || hasEmoji || available.includes(res.content.toLowerCase()); - }; - const p2Color = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (p2Color.size) { - const choice = p2Color.first().content.toLowerCase(); - const hasCustom = choice.match(customEmojiRegex); - if (hasCustom && msg.guild) { - playerTwoEmoji = msg.guild.emojis.cache.get(hasCustom[2]).toString(); - } else { - playerTwoEmoji = colors[choice] || choice; - } + const available = Object.keys(colors).filter(clr => color !== colors[clr]); + if (opponent.bot) { + playerTwoEmoji = colors[available[Math.floor(Math.random() * available.length)]]; + } else { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + await msg.say( + `${opponent}, what color do you want to be? Either an emoji or one of ${list(available, 'or')}.` + ); + const filter = res => { + if (res.author.id !== opponent.id) return false; + if (res.content === blankEmoji) return false; + const hasEmoji = new RegExp(`^(?:${emojiRegex().source})$`).test(res.content); + const hasCustom = res.content.match(customEmojiRegex); + if (hasCustom && msg.guild && !msg.guild.emojis.cache.has(hasCustom[2])) return false; + return (hasCustom && msg.guild) || hasEmoji || available.includes(res.content.toLowerCase()); + }; + const p2Color = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (p2Color.size) { + const choice = p2Color.first().content.toLowerCase(); + const hasCustom = choice.match(customEmojiRegex); + if (hasCustom && msg.guild) { + playerTwoEmoji = msg.guild.emojis.cache.get(hasCustom[2]).toString(); + } else { + playerTwoEmoji = colors[choice] || choice; } } - const AIEngine = new Connect4AI(); - const board = this.generateBoard(); - let userTurn = true; - let winner = null; - const colLevels = [5, 5, 5, 5, 5, 5, 5]; - let lastMove = 'None'; - while (!winner && board.some(row => row.includes(null))) { - const user = userTurn ? msg.author : opponent; - const sign = userTurn ? 'user' : 'oppo'; - let i; - if (opponent.bot && !userTurn) { + } + const AIEngine = new Connect4AI(); + const board = this.generateBoard(); + let userTurn = true; + let winner = null; + const colLevels = [5, 5, 5, 5, 5, 5, 5]; + let lastMove = 'None'; + while (!winner && board.some(row => row.includes(null))) { + const user = userTurn ? msg.author : opponent; + const sign = userTurn ? 'user' : 'oppo'; + let i; + if (opponent.bot && !userTurn) { + i = AIEngine.playAI('hard'); + lastMove = i + 1; + } else { + const emoji = userTurn ? playerOneEmoji : playerTwoEmoji; + await msg.say(stripIndents` + ${emoji} ${user}, which column do you pick? Type \`end\` to forfeit. + Can't think of a move? Use \`play for me\`. + ${opponent.bot ? `I placed mine in **${lastMove}**.` : `Previous Move: **${lastMove}**`} + + ${this.displayBoard(board, playerOneEmoji, playerTwoEmoji)} + ${nums.join('')} + `); + const pickFilter = res => { + if (res.author.id !== user.id) return false; + const choice = res.content; + if (choice.toLowerCase() === 'end') return true; + if (choice.toLowerCase() === 'play for me') return true; + const j = Number.parseInt(choice, 10) - 1; + return board[colLevels[j]] && board[colLevels[j]][j] !== undefined; + }; + const turn = await msg.channel.awaitMessages({ + filter: pickFilter, + max: 1, + time: 60000 + }); + 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'); + lastMove = i + 1; + } else if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } else if (choice.toLowerCase() === 'play for me') { i = AIEngine.playAI('hard'); lastMove = i + 1; } else { - const emoji = userTurn ? playerOneEmoji : playerTwoEmoji; - await msg.say(stripIndents` - ${emoji} ${user}, which column do you pick? Type \`end\` to forfeit. - Can't think of a move? Use \`play for me\`. - ${opponent.bot ? `I placed mine in **${lastMove}**.` : `Previous Move: **${lastMove}**`} - - ${this.displayBoard(board, playerOneEmoji, playerTwoEmoji)} - ${nums.join('')} - `); - const pickFilter = res => { - if (res.author.id !== user.id) return false; - const choice = res.content; - if (choice.toLowerCase() === 'end') return true; - if (choice.toLowerCase() === 'play for me') return true; - const j = Number.parseInt(choice, 10) - 1; - return board[colLevels[j]] && board[colLevels[j]][j] !== undefined; - }; - const turn = await msg.channel.awaitMessages({ - filter: pickFilter, - max: 1, - time: 60000 - }); - 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'); - lastMove = i + 1; - } else if (choice.toLowerCase() === 'end') { - winner = userTurn ? opponent : msg.author; - break; - } else if (choice.toLowerCase() === 'play for me') { - i = AIEngine.playAI('hard'); - lastMove = i + 1; - } else { - i = Number.parseInt(choice, 10) - 1; - AIEngine.play(i); - lastMove = i + 1; - } + i = Number.parseInt(choice, 10) - 1; + AIEngine.play(i); + lastMove = i + 1; } - board[colLevels[i]][i] = sign; - colLevels[i]--; - if (this.verifyWin(board)) winner = userTurn ? msg.author : opponent; - userTurn = !userTurn; } - this.client.games.delete(msg.channel.id); - if (winner === 'time') return msg.say('Game ended due to inactivity.'); - return msg.say(stripIndents` + board[colLevels[i]][i] = sign; + colLevels[i]--; + if (this.verifyWin(board)) winner = userTurn ? msg.author : opponent; + userTurn = !userTurn; + } + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(stripIndents` ${winner ? `Congrats, ${winner}!` : 'Looks like it\'s a draw...'} Final Move: **${lastMove}** ${this.displayBoard(board, playerOneEmoji, playerTwoEmoji)} ${nums.join('')} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; - } } checkLine(a, b, c, d) { diff --git a/commands/games-mp/cram.js b/commands/games-mp/cram.js index 3c05b910..77599fb8 100644 --- a/commands/games-mp/cram.js +++ b/commands/games-mp/cram.js @@ -15,6 +15,7 @@ module.exports = class CramCommand extends Command { memberName: 'cram', description: 'Play a game of Cram with another user.', guildOnly: true, + game: true, args: [ { key: 'opponent', @@ -41,102 +42,90 @@ module.exports = class CramCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 }); const userEmoji = colors[color]; let oppoEmoji = userEmoji === colors.blue ? colors.red : colors.blue; - try { - const available = Object.keys(colors).filter(clr => color !== clr); - 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...'); - } - await msg.say(`${opponent}, what color do you want to be? Either ${list(available, 'or')}.`); - const filter = res => { - if (res.author.id !== opponent.id) return false; - return available.includes(res.content.toLowerCase()); - }; - const p2Color = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (p2Color.size) oppoEmoji = colors[p2Color.first().content.toLowerCase()]; - const board = this.generateBoard(size); - let userTurn = true; - let winner = null; - let lastTurnTimeout = false; - while (!winner) { - const user = userTurn ? msg.author : opponent; - await msg.say(stripIndents` - ${user}, at what coordinates do you want to place your block? Type \`end\` to forfeit. - You must also choose a direction. (ex. v1,1 or h3,4). - - ${this.displayBoard(board, userEmoji, oppoEmoji)} - `); - const possibleMoves = this.possibleMoves(board); - const colorFilter = res => { - if (res.author.id !== user.id) return false; - const pick = res.content; - if (pick.toLowerCase() === 'end') return true; - const coordPicked = pick.match(turnRegex); - if (!coordPicked) return false; - const direction = coordPicked[1].toLowerCase(); - const x = Number.parseInt(coordPicked[2], 10); - const y = Number.parseInt(coordPicked[3], 10); - if (x > size || y > size || x < 1 || y < 1) return false; - if (!possibleMoves.includes(`${direction}${x - 1},${y - 1}`)) return false; - return true; - }; - const turn = await msg.channel.awaitMessages({ - filter: colorFilter, - max: 1, - time: 60000 - }); - if (!turn.size) { - await msg.say('Sorry, time is up!'); - if (lastTurnTimeout) { - winner = 'time'; - break; - } else { - lastTurnTimeout = true; - userTurn = !userTurn; - continue; - } - } - const choice = turn.first().content; - if (choice.toLowerCase() === 'end') { - winner = userTurn ? opponent : msg.author; - break; - } - const matched = choice.match(turnRegex); - const direction = matched[1].toLowerCase(); - const x = Number.parseInt(matched[2], 10); - const y = Number.parseInt(matched[3], 10); - board[y - 1][x - 1] = userTurn ? 'U' : 'O'; - board[direction === 'v' ? y : y - 1][direction === 'v' ? x - 1 : x] = userTurn ? 'U' : 'O'; - userTurn = !userTurn; - if (lastTurnTimeout) lastTurnTimeout = false; - const oppoPossible = this.possibleMoves(board, userTurn); - if (!oppoPossible.length) { - winner = userTurn ? opponent : msg.author; - break; - } - } - this.client.games.delete(msg.channel.id); - if (winner === 'time') return msg.say('Game ended due to inactivity.'); - return msg.say(stripIndents` - Congrats, ${winner}! Your opponent has no possible moves left! + const available = Object.keys(colors).filter(clr => color !== clr); + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + await msg.say(`${opponent}, what color do you want to be? Either ${list(available, 'or')}.`); + const filter = res => { + if (res.author.id !== opponent.id) return false; + return available.includes(res.content.toLowerCase()); + }; + const p2Color = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (p2Color.size) oppoEmoji = colors[p2Color.first().content.toLowerCase()]; + const board = this.generateBoard(size); + let userTurn = true; + let winner = null; + let lastTurnTimeout = false; + while (!winner) { + const user = userTurn ? msg.author : opponent; + await msg.say(stripIndents` + ${user}, at what coordinates do you want to place your block? Type \`end\` to forfeit. + You must also choose a direction. (ex. v1,1 or h3,4). ${this.displayBoard(board, userEmoji, oppoEmoji)} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const possibleMoves = this.possibleMoves(board); + const colorFilter = res => { + if (res.author.id !== user.id) return false; + const pick = res.content; + if (pick.toLowerCase() === 'end') return true; + const coordPicked = pick.match(turnRegex); + if (!coordPicked) return false; + const direction = coordPicked[1].toLowerCase(); + const x = Number.parseInt(coordPicked[2], 10); + const y = Number.parseInt(coordPicked[3], 10); + if (x > size || y > size || x < 1 || y < 1) return false; + if (!possibleMoves.includes(`${direction}${x - 1},${y - 1}`)) return false; + return true; + }; + const turn = await msg.channel.awaitMessages({ + filter: colorFilter, + max: 1, + time: 60000 + }); + if (!turn.size) { + await msg.say('Sorry, time is up!'); + if (lastTurnTimeout) { + winner = 'time'; + break; + } else { + lastTurnTimeout = true; + userTurn = !userTurn; + continue; + } + } + const choice = turn.first().content; + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + const matched = choice.match(turnRegex); + const direction = matched[1].toLowerCase(); + const x = Number.parseInt(matched[2], 10); + const y = Number.parseInt(matched[3], 10); + board[y - 1][x - 1] = userTurn ? 'U' : 'O'; + board[direction === 'v' ? y : y - 1][direction === 'v' ? x - 1 : x] = userTurn ? 'U' : 'O'; + userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; + const oppoPossible = this.possibleMoves(board, userTurn); + if (!oppoPossible.length) { + winner = userTurn ? opponent : msg.author; + break; + } } + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(stripIndents` + Congrats, ${winner}! Your opponent has no possible moves left! + + ${this.displayBoard(board, userEmoji, oppoEmoji)} + `); } possibleMoves(board) { diff --git a/commands/games-mp/domineering.js b/commands/games-mp/domineering.js index 51ff3b80..0f52070f 100644 --- a/commands/games-mp/domineering.js +++ b/commands/games-mp/domineering.js @@ -15,6 +15,7 @@ module.exports = class DomineeringCommand extends Command { memberName: 'domineering', description: 'Play a game of Domineering with another user.', guildOnly: true, + game: true, args: [ { key: 'opponent', @@ -41,100 +42,88 @@ module.exports = class DomineeringCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 }); const userEmoji = colors[color]; let oppoEmoji = userEmoji === colors.blue ? colors.red : colors.blue; - try { - const available = Object.keys(colors).filter(clr => color !== clr); - 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...'); - } - await msg.say(`${opponent}, what color do you want to be? Either ${list(available, 'or')}.`); - const filter = res => { - if (res.author.id !== opponent.id) return false; - return available.includes(res.content.toLowerCase()); - }; - const p2Color = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (p2Color.size) oppoEmoji = colors[p2Color.first().content.toLowerCase()]; - const board = this.generateBoard(size); - let userTurn = true; - let winner = null; - let lastTurnTimeout = false; - while (!winner) { - const user = userTurn ? msg.author : opponent; - await msg.say(stripIndents` - ${user}, at what coordinates do you want to place your block (ex. 1,1)? Type \`end\` to forfeit. - Your pieces are **${userTurn ? 'vertical' : 'horizontal'}**. - - ${this.displayBoard(board, userEmoji, oppoEmoji)} - `); - const possibleMoves = this.possibleMoves(board, userTurn); - const colorFilter = res => { - if (res.author.id !== user.id) return false; - const pick = res.content; - if (pick.toLowerCase() === 'end') return true; - const coordPicked = pick.match(turnRegex); - if (!coordPicked) return false; - const x = Number.parseInt(coordPicked[1], 10); - const y = Number.parseInt(coordPicked[2], 10); - if (x > size || y > size || x < 1 || y < 1) return false; - if (!possibleMoves.includes(`${x - 1},${y - 1}`)) return false; - return true; - }; - const turn = await msg.channel.awaitMessages({ - filter: colorFilter, - max: 1, - time: 60000 - }); - if (!turn.size) { - await msg.say('Sorry, time is up!'); - if (lastTurnTimeout) { - winner = 'time'; - break; - } else { - lastTurnTimeout = true; - userTurn = !userTurn; - continue; - } - } - const choice = turn.first().content; - if (choice.toLowerCase() === 'end') { - winner = userTurn ? opponent : msg.author; - break; - } - const matched = choice.match(turnRegex); - const x = Number.parseInt(matched[1], 10); - const y = Number.parseInt(matched[2], 10); - board[y - 1][x - 1] = userTurn ? 'U' : 'O'; - board[userTurn ? y : y - 1][userTurn ? x - 1 : x] = userTurn ? 'U' : 'O'; - userTurn = !userTurn; - if (lastTurnTimeout) lastTurnTimeout = false; - const oppoPossible = this.possibleMoves(board, userTurn); - if (!oppoPossible.length) { - winner = userTurn ? opponent : msg.author; - break; - } - } - this.client.games.delete(msg.channel.id); - if (winner === 'time') return msg.say('Game ended due to inactivity.'); - return msg.say(stripIndents` - Congrats, ${winner}! Your opponent has no possible moves left! + const available = Object.keys(colors).filter(clr => color !== clr); + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + await msg.say(`${opponent}, what color do you want to be? Either ${list(available, 'or')}.`); + const filter = res => { + if (res.author.id !== opponent.id) return false; + return available.includes(res.content.toLowerCase()); + }; + const p2Color = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (p2Color.size) oppoEmoji = colors[p2Color.first().content.toLowerCase()]; + const board = this.generateBoard(size); + let userTurn = true; + let winner = null; + let lastTurnTimeout = false; + while (!winner) { + const user = userTurn ? msg.author : opponent; + await msg.say(stripIndents` + ${user}, at what coordinates do you want to place your block (ex. 1,1)? Type \`end\` to forfeit. + Your pieces are **${userTurn ? 'vertical' : 'horizontal'}**. ${this.displayBoard(board, userEmoji, oppoEmoji)} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const possibleMoves = this.possibleMoves(board, userTurn); + const colorFilter = res => { + if (res.author.id !== user.id) return false; + const pick = res.content; + if (pick.toLowerCase() === 'end') return true; + const coordPicked = pick.match(turnRegex); + if (!coordPicked) return false; + const x = Number.parseInt(coordPicked[1], 10); + const y = Number.parseInt(coordPicked[2], 10); + if (x > size || y > size || x < 1 || y < 1) return false; + if (!possibleMoves.includes(`${x - 1},${y - 1}`)) return false; + return true; + }; + const turn = await msg.channel.awaitMessages({ + filter: colorFilter, + max: 1, + time: 60000 + }); + if (!turn.size) { + await msg.say('Sorry, time is up!'); + if (lastTurnTimeout) { + winner = 'time'; + break; + } else { + lastTurnTimeout = true; + userTurn = !userTurn; + continue; + } + } + const choice = turn.first().content; + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + const matched = choice.match(turnRegex); + const x = Number.parseInt(matched[1], 10); + const y = Number.parseInt(matched[2], 10); + board[y - 1][x - 1] = userTurn ? 'U' : 'O'; + board[userTurn ? y : y - 1][userTurn ? x - 1 : x] = userTurn ? 'U' : 'O'; + userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; + const oppoPossible = this.possibleMoves(board, userTurn); + if (!oppoPossible.length) { + winner = userTurn ? opponent : msg.author; + break; + } } + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(stripIndents` + Congrats, ${winner}! Your opponent has no possible moves left! + + ${this.displayBoard(board, userEmoji, oppoEmoji)} + `); } possibleMoves(board, userTurn) { diff --git a/commands/games-mp/dots-and-boxes.js b/commands/games-mp/dots-and-boxes.js index 6cf718f6..79482b54 100644 --- a/commands/games-mp/dots-and-boxes.js +++ b/commands/games-mp/dots-and-boxes.js @@ -12,6 +12,7 @@ module.exports = class DotsAndBoxesCommand extends Command { memberName: 'dots-and-boxes', description: 'Play a game of Dots and Boxes with another user.', guildOnly: true, + game: true, args: [ { key: 'opponent', @@ -25,113 +26,101 @@ module.exports = class DotsAndBoxesCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - 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 = this.generateBoard(); - const taken = []; - const userOwned = []; - const oppoOwned = []; - let userTurn = true; - let winner = null; - let lastTurnTimeout = false; - while (taken.length < 40) { - const user = userTurn ? msg.author : opponent; - await msg.say(stripIndents` - ${user}, which connection do you pick? Type \`end\` to forfeit. - _Format like \`1-2\` or \`0-5\`. Any two spaces bordering **vertical or horizontal**._ + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + const board = this.generateBoard(); + const taken = []; + const userOwned = []; + const oppoOwned = []; + let userTurn = true; + let winner = null; + let lastTurnTimeout = false; + while (taken.length < 40) { + const user = userTurn ? msg.author : opponent; + await msg.say(stripIndents` + ${user}, which connection do you pick? Type \`end\` to forfeit. + _Format like \`1-2\` or \`0-5\`. Any two spaces bordering **vertical or horizontal**._ - P1: ${msg.author.tag} | P2: ${opponent.tag} - \`\`\` - ${this.displayBoard(board, taken, userOwned, oppoOwned)} - \`\`\` - `); - const filter = res => { - if (res.author.id !== user.id) return false; - const choice = res.content; - if (choice.toLowerCase() === 'end') return true; - const matched = choice.match(/([0-9]+)-([0-9]+)/); - if (!matched) return false; - let first = Number.parseInt(matched[1], 10); - let second = Number.parseInt(matched[2], 10); - if (first === second) return false; - if (second < first) { - const temp = first; - first = second; - second = temp; - } - if (first > 24 || second > 24 || first < 0 || second < 0) return false; - const column1 = first % 5; - const column2 = second % 5; - if (second !== first + 1 && column1 !== column2) { - const row1 = Math.floor(first / 5); - const row2 = Math.floor(second / 5); - if (row2 !== row1 - 1) return false; - return !taken.includes(`${first}-${second}`); - } - return !taken.includes(`${first}-${second}`); - }; - const turn = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 60000 - }); - if (!turn.size) { - await msg.say('Sorry, time is up!'); - if (lastTurnTimeout) { - winner = 'time'; - break; - } else { - lastTurnTimeout = true; - userTurn = !userTurn; - continue; - } - } - const choice = turn.first().content; - if (choice.toLowerCase() === 'end') { - winner = userTurn ? opponent : msg.author; - break; - } + P1: ${msg.author.tag} | P2: ${opponent.tag} + \`\`\` + ${this.displayBoard(board, taken, userOwned, oppoOwned)} + \`\`\` + `); + const filter = res => { + if (res.author.id !== user.id) return false; + const choice = res.content; + if (choice.toLowerCase() === 'end') return true; const matched = choice.match(/([0-9]+)-([0-9]+)/); + if (!matched) return false; let first = Number.parseInt(matched[1], 10); let second = Number.parseInt(matched[2], 10); + if (first === second) return false; if (second < first) { const temp = first; first = second; second = temp; } - taken.push(`${first}-${second}`); - const newSquares = this.calcNewSquare(taken, userOwned, oppoOwned); - if (newSquares.length) { - for (const newSquare of newSquares) { - if (userTurn) userOwned.push(newSquare); - else oppoOwned.push(newSquare); - } - if (taken.length < 40) { - await msg.say(`${user}, great job! Keep going until you can't make any more!`); - } - } else { - userTurn = !userTurn; + if (first > 24 || second > 24 || first < 0 || second < 0) return false; + const column1 = first % 5; + const column2 = second % 5; + if (second !== first + 1 && column1 !== column2) { + const row1 = Math.floor(first / 5); + const row2 = Math.floor(second / 5); + if (row2 !== row1 - 1) return false; + return !taken.includes(`${first}-${second}`); + } + return !taken.includes(`${first}-${second}`); + }; + const turn = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 60000 + }); + if (!turn.size) { + await msg.say('Sorry, time is up!'); + if (lastTurnTimeout) { + winner = 'time'; + break; + } else { + lastTurnTimeout = true; + userTurn = !userTurn; + continue; } - if (lastTurnTimeout) lastTurnTimeout = false; } - this.client.games.delete(msg.channel.id); - if (winner === 'time') return msg.say('Game ended due to inactivity.'); - winner = userOwned.length === oppoOwned.length - ? null - : userOwned.length > oppoOwned.length ? msg.author : opponent; - return msg.say(winner ? `Congrats, ${winner}!` : 'Looks like it\'s a draw...'); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const choice = turn.first().content; + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + const matched = choice.match(/([0-9]+)-([0-9]+)/); + let first = Number.parseInt(matched[1], 10); + let second = Number.parseInt(matched[2], 10); + if (second < first) { + const temp = first; + first = second; + second = temp; + } + taken.push(`${first}-${second}`); + const newSquares = this.calcNewSquare(taken, userOwned, oppoOwned); + if (newSquares.length) { + for (const newSquare of newSquares) { + if (userTurn) userOwned.push(newSquare); + else oppoOwned.push(newSquare); + } + if (taken.length < 40) { + await msg.say(`${user}, great job! Keep going until you can't make any more!`); + } + } else { + userTurn = !userTurn; + } + if (lastTurnTimeout) lastTurnTimeout = false; } + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + winner = userOwned.length === oppoOwned.length + ? null + : userOwned.length > oppoOwned.length ? msg.author : opponent; + return msg.say(winner ? `Congrats, ${winner}!` : 'Looks like it\'s a draw...'); } calcSquare(num, taken) { diff --git a/commands/games-mp/emoji-emoji-revolution.js b/commands/games-mp/emoji-emoji-revolution.js index d7983f79..c70288dd 100644 --- a/commands/games-mp/emoji-emoji-revolution.js +++ b/commands/games-mp/emoji-emoji-revolution.js @@ -13,6 +13,7 @@ module.exports = class EmojiEmojiRevolutionCommand extends Command { memberName: 'emoji-emoji-revolution', description: 'Can you type arrow emoji faster than anyone else has ever typed them before?', guildOnly: true, + game: true, credit: [ { name: 'Dance Dance Revolution', @@ -33,57 +34,45 @@ module.exports = class EmojiEmojiRevolutionCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - 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...'); - } - let turn = 0; - let aPts = 0; - let oPts = 0; - let lastTurnTimeout = false; - while (turn < 10) { - ++turn; - const num = Math.floor(Math.random() * emojis.length); - const emoji = [emojis[num], emojisNew[num]]; - await msg.say(emojisNew[num]); - const filter = res => [msg.author.id, opponent.id].includes(res.author.id) && emoji.includes(res.content); - const win = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (!win.size) { - await msg.say('Hmm... No one even tried that round.'); - if (lastTurnTimeout) { - break; - } else { - lastTurnTimeout = true; - continue; - } + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + let turn = 0; + let aPts = 0; + let oPts = 0; + let lastTurnTimeout = false; + while (turn < 10) { + ++turn; + const num = Math.floor(Math.random() * emojis.length); + const emoji = [emojis[num], emojisNew[num]]; + await msg.say(emojisNew[num]); + const filter = res => [msg.author.id, opponent.id].includes(res.author.id) && emoji.includes(res.content); + const win = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (!win.size) { + await msg.say('Hmm... No one even tried that round.'); + if (lastTurnTimeout) { + break; + } else { + lastTurnTimeout = true; + continue; } - const winner = win.first().author; - if (winner.id === msg.author.id) ++aPts; - else ++oPts; - await msg.say(stripIndents` - ${winner} won this round! - **${msg.author.username}:** ${aPts} - **${opponent.username}:** ${oPts} - `); - if (lastTurnTimeout) lastTurnTimeout = false; } - this.client.games.delete(msg.channel.id); - if (aPts === oPts) return msg.say('It\'s a tie!'); - const userWin = aPts > oPts; - return msg.say(`You win ${userWin ? msg.author : opponent} with ${userWin ? aPts : oPts} points!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const winner = win.first().author; + if (winner.id === msg.author.id) ++aPts; + else ++oPts; + await msg.say(stripIndents` + ${winner} won this round! + **${msg.author.username}:** ${aPts} + **${opponent.username}:** ${oPts} + `); + if (lastTurnTimeout) lastTurnTimeout = false; } + if (aPts === oPts) return msg.say('It\'s a tie!'); + const userWin = aPts > oPts; + return msg.say(`You win ${userWin ? msg.author : opponent} with ${userWin ? aPts : oPts} points!`); } }; diff --git a/commands/games-mp/guesspionage.js b/commands/games-mp/guesspionage.js index 28e4f2a0..4e84290b 100644 --- a/commands/games-mp/guesspionage.js +++ b/commands/games-mp/guesspionage.js @@ -16,6 +16,7 @@ module.exports = class GuesspionageCommand extends Command { memberName: 'guesspionage', description: 'Tests your knowledge of humans as you guess how people responded to poll questions.', guildOnly: true, + game: true, credit: [ { name: 'Jackbox Games', @@ -49,120 +50,108 @@ module.exports = class GuesspionageCommand extends Command { } async run(msg, { players }) { - 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 { - const awaitedPlayers = await awaitPlayers(msg, players, min, this.client.blacklist.user); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); - } - let turn = 0; - const pts = new Collection(); - for (const player of awaitedPlayers) { - pts.set(player, { - points: 0, - id: player, - user: await this.client.users.fetch(player) - }); - } - const used = []; - const userTurn = awaitedPlayers.slice(0); - let lastTurnTimeout = false; - while (userTurn.length) { - ++turn; - const mainUser = pts.get(userTurn[0]).user; - userTurn.shift(); - const valid = questions.filter( - question => !used.includes(question.text) && (msg.channel.nsfw ? true : !question.nsfw) - ); - const question = valid[Math.floor(Math.random() * valid.length)]; - used.push(question.text); - await msg.say(stripIndents` - **${turn}.** ${question.text} + const awaitedPlayers = await awaitPlayers(msg, players, min, this.client.blacklist.user); + if (!awaitedPlayers) return msg.say('Game could not be started...'); + let turn = 0; + const pts = new Collection(); + for (const player of awaitedPlayers) { + pts.set(player, { + points: 0, + id: player, + user: await this.client.users.fetch(player) + }); + } + const used = []; + const userTurn = awaitedPlayers.slice(0); + let lastTurnTimeout = false; + while (userTurn.length) { + ++turn; + const mainUser = pts.get(userTurn[0]).user; + userTurn.shift(); + const valid = questions.filter( + question => !used.includes(question.text) && (msg.channel.nsfw ? true : !question.nsfw) + ); + const question = valid[Math.floor(Math.random() * valid.length)]; + used.push(question.text); + await msg.say(stripIndents` + **${turn}.** ${question.text} - ${mainUser}, what percentage do you guess? - `); - const initialGuessFilter = res => { - if (res.author.id !== mainUser.id) return false; - const int = Number.parseInt(res.content, 10); - return int >= 0 && int <= 100; - }; - const initialGuess = await msg.channel.awaitMessages({ - filter: initialGuessFilter, - max: 1, - time: 30000 - }); - if (!initialGuess.size) { - await msg.say('Hmm... No guess? I guess you\'re getting skipped.'); + ${mainUser}, what percentage do you guess? + `); + const initialGuessFilter = res => { + if (res.author.id !== mainUser.id) return false; + const int = Number.parseInt(res.content, 10); + return int >= 0 && int <= 100; + }; + const initialGuess = await msg.channel.awaitMessages({ + filter: initialGuessFilter, + max: 1, + time: 30000 + }); + if (!initialGuess.size) { + await msg.say('Hmm... No guess? I guess you\'re getting skipped.'); + continue; + } + const guess = Number.parseInt(initialGuess.first().content, 10); + await msg.say(stripIndents` + **${guess}%** + + Alright, everyone else, do you think the _actual_ percentage is \`higher\` or \`lower\`? + You can also guess \`much higher\` or \`much lower\` for double points if their answer is 15% off. + `); + const guessed = []; + const everyoneElseFilter = res => { + if (res.author.id === mainUser.id) return false; + if (guessed.includes(res.author.id)) return false; + if (!awaitedPlayers.includes(res.author.id)) return false; + if (!guesses.includes(res.content.toLowerCase())) return false; + guessed.push(res.author.id); + reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); + return true; + }; + const everyoneElse = await msg.channel.awaitMessages({ + filter: everyoneElseFilter, + max: awaitedPlayers.length - 1, + time: 30000 + }); + if (!everyoneElse.size) { + if (lastTurnTimeout) { + await msg.say('Game ended due to inactivity.'); + break; + } else { + await msg.say('Come on guys, get in the game!'); + lastTurnTimeout = true; continue; } - const guess = Number.parseInt(initialGuess.first().content, 10); - await msg.say(stripIndents` - **${guess}%** - - Alright, everyone else, do you think the _actual_ percentage is \`higher\` or \`lower\`? - You can also guess \`much higher\` or \`much lower\` for double points if their answer is 15% off. - `); - const guessed = []; - const everyoneElseFilter = res => { - if (res.author.id === mainUser.id) return false; - if (guessed.includes(res.author.id)) return false; - if (!awaitedPlayers.includes(res.author.id)) return false; - if (!guesses.includes(res.content.toLowerCase())) return false; - guessed.push(res.author.id); - reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); - return true; - }; - const everyoneElse = await msg.channel.awaitMessages({ - filter: everyoneElseFilter, - max: awaitedPlayers.length - 1, - time: 30000 - }); - if (!everyoneElse.size) { - if (lastTurnTimeout) { - await msg.say('Game ended due to inactivity.'); - break; - } else { - await msg.say('Come on guys, get in the game!'); - lastTurnTimeout = true; - continue; - } - } - const higherLower = everyoneElse.map(res => ({ guess: res.content.toLowerCase(), id: res.author.id })); - for (const answer of higherLower) { - const uGuess = answer.guess; - if (uGuess === 'higher' && guess < question.answer) { - pts.get(answer.id).points += 1000; - } else if (uGuess === 'lower' && guess > question.answer) { - pts.get(answer.id).points += 1000; - } else if (uGuess === 'much higher' && guess < question.answer && question.answer - guess >= 15) { - pts.get(answer.id).points += 2000; - } else if (uGuess === 'much lower' && guess > question.answer && guess - question.answer >= 15) { - pts.get(answer.id).points += 2000; - } - } - const diff = Math.abs(question.answer - guess); - if (diff <= 30) pts.get(mainUser.id).points += 3000 - (diff * 100); - await msg.say(stripIndents` - The actual answer was... **${question.answer}%**! - - __**Leaderboard:**__ - ${this.makeLeaderboard(pts).join('\n')} - - ${userTurn.length ? '_Next round starting in 10 seconds..._' : ''} - `); - if (lastTurnTimeout) lastTurnTimeout = false; - if (userTurn.length) await delay(10000); } - this.client.games.delete(msg.channel.id); - const winner = pts.sort((a, b) => b.points - a.points).first().user; - return msg.say(`Congrats, ${winner}!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const higherLower = everyoneElse.map(res => ({ guess: res.content.toLowerCase(), id: res.author.id })); + for (const answer of higherLower) { + const uGuess = answer.guess; + if (uGuess === 'higher' && guess < question.answer) { + pts.get(answer.id).points += 1000; + } else if (uGuess === 'lower' && guess > question.answer) { + pts.get(answer.id).points += 1000; + } else if (uGuess === 'much higher' && guess < question.answer && question.answer - guess >= 15) { + pts.get(answer.id).points += 2000; + } else if (uGuess === 'much lower' && guess > question.answer && guess - question.answer >= 15) { + pts.get(answer.id).points += 2000; + } + } + const diff = Math.abs(question.answer - guess); + if (diff <= 30) pts.get(mainUser.id).points += 3000 - (diff * 100); + await msg.say(stripIndents` + The actual answer was... **${question.answer}%**! + + __**Leaderboard:**__ + ${this.makeLeaderboard(pts).join('\n')} + + ${userTurn.length ? '_Next round starting in 10 seconds..._' : ''} + `); + if (lastTurnTimeout) lastTurnTimeout = false; + if (userTurn.length) await delay(10000); } + const winner = pts.sort((a, b) => b.points - a.points).first().user; + return msg.say(`Congrats, ${winner}!`); } makeLeaderboard(pts) { diff --git a/commands/games-mp/gunfight.js b/commands/games-mp/gunfight.js index a6d7e09b..0ecf670c 100644 --- a/commands/games-mp/gunfight.js +++ b/commands/games-mp/gunfight.js @@ -12,6 +12,7 @@ module.exports = class GunfightCommand extends Command { memberName: 'gunfight', description: 'Engage in a western gunfight against another user. High noon.', guildOnly: true, + game: true, args: [ { key: 'opponent', @@ -25,46 +26,34 @@ module.exports = class GunfightCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be fought.'); if (opponent.id === msg.author.id) return msg.reply('You may not fight yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - 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...'); - } - await msg.say('Get Ready...'); - await delay(randomRange(1000, 30000)); - const word = words[Math.floor(Math.random() * words.length)]; - await msg.say(`TYPE \`${word.toUpperCase()}\` NOW!`); - const filter = res => [opponent.id, msg.author.id].includes(res.author.id) && res.content.toLowerCase() === word; - const now = Date.now(); - const winner = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - const newScore = Date.now() - now; - const highScoreGet = await this.client.redis.get('reaction-time'); - const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; - const highScoreUser = await this.client.redis.get('reaction-time-user'); - const scoreBeat = !highScore || highScore > newScore; - const user = await fetchHSUserDisplay(this.client, highScoreUser); - if (scoreBeat) { - await this.client.redis.set('reaction-time', newScore); - await this.client.redis.set('reaction-time-user', winner.first().author.id); - } - this.client.games.delete(msg.channel.id); - if (!winner.size) return msg.say('Oh... No one won.'); - return msg.say(stripIndents` - The winner is ${winner.first().author}! (Took ${newScore / 1000} seconds) - ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore / 1000} (Held by ${user}) - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + await msg.say('Get Ready...'); + await delay(randomRange(1000, 30000)); + const word = words[Math.floor(Math.random() * words.length)]; + await msg.say(`TYPE \`${word.toUpperCase()}\` NOW!`); + const filter = res => [opponent.id, msg.author.id].includes(res.author.id) && res.content.toLowerCase() === word; + const now = Date.now(); + const winner = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + const newScore = Date.now() - now; + const highScoreGet = await this.client.redis.get('reaction-time'); + const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; + const highScoreUser = await this.client.redis.get('reaction-time-user'); + const scoreBeat = !highScore || highScore > newScore; + const user = await fetchHSUserDisplay(this.client, highScoreUser); + if (scoreBeat) { + await this.client.redis.set('reaction-time', newScore); + await this.client.redis.set('reaction-time-user', winner.first().author.id); } + if (!winner.size) return msg.say('Oh... No one won.'); + return msg.say(stripIndents` + The winner is ${winner.first().author}! (Took ${newScore / 1000} seconds) + ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore / 1000} (Held by ${user}) + `); } }; diff --git a/commands/games-mp/imposter.js b/commands/games-mp/imposter.js index fef89f4b..84b59e1e 100644 --- a/commands/games-mp/imposter.js +++ b/commands/games-mp/imposter.js @@ -14,6 +14,7 @@ module.exports = class ImposterCommand extends Command { memberName: 'imposter', description: 'Who is the imposter among us?', guildOnly: true, + game: true, clientPermissions: ['ADD_REACTIONS', 'READ_MESSAGE_HISTORY'], args: [ { @@ -28,138 +29,126 @@ module.exports = class ImposterCommand extends Command { } async run(msg, { playersCount }) { - 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 { - const awaitedPlayers = await awaitPlayers(msg, playersCount, 3, this.client.blacklist.user); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); - } - const word = words[Math.floor(Math.random() * words.length)]; - const wordRegex = new RegExp(`\\b${word}\\b`, 'i'); - const players = new Collection(); - const imposter = awaitedPlayers[Math.floor(Math.random() * awaitedPlayers.length)]; - await msg.say(oneLine` - Welcome to Imposter! In this game, you will have to figure out who the imposter is! - All you have to do is watch what other players say. There's a special word called a kill word. - Only the imposter can say it, and if anyone else does, they die! To win, figure out what the kill - word is, and try to catch the imposter saying it. As for the imposter, you know the word, try to get - everyone to say it! - `); - for (const player of awaitedPlayers) { - players.set(player, { - id: player, - user: await this.client.users.fetch(player), - killed: false, - imposter: imposter === player - }); - const newPlayer = players.get(player); - if (imposter === player) newPlayer.user.send(`You are the imposter. The kill word is ${word}.`); - else newPlayer.user.send('You are not the imposter. Be careful what you say!'); - } - let lastTurnTimeout = false; - const winners = []; - while (players.filter(player => !player.killed).size > 2) { - const playersLeft = players.filter(player => !player.killed).size; - await msg.say(`There are **${playersLeft}** players left. Talk until someone says the kill word.`); - const filter = res => { - const player = players.get(res.author.id); - if (!player || player.killed || player.imposter) return false; - if (res.content && wordRegex.test(res.content)) return true; - return false; - }; - const msgs = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 600000 - }); - if (msgs.size) { - const killedMsg = msgs.first(); - try { - await killedMsg.react('🔪'); - } catch { - await killedMsg.reply('🔪'); - } - players.get(killedMsg.author.id).killed = true; - await msg.say(stripIndents` - ${killedMsg.author} has been murdered for saying the kill word! - Talk amongst yourselves, who is the imposter? Voting begins in 1 minute. - `); - } else { - await msg.say(stripIndents` - No has said the word for 10 minutes. We're voting anyway! Who looks suspicious? - Talk amongst yourselves, who is the imposter? Voting begins in 1 minute. - `); - } - await delay(60000); - const choices = players.filter(player => !player.killed); - const ids = choices.map(player => player.id); - let i = 0; - await msg.say(stripIndents` - Alright, who do you think the imposter is? You have 1 minute to vote. - - _Type the number of the player you think is the imposter._ - ${choices.map(player => { i++; return `**${i}.** ${player.user.tag}`; }).join('\n')} - `); - const votes = new Collection(); - const voteFilter = res => { - const player = players.get(res.author.id); - if (!player || player.killed) return false; - const int = Number.parseInt(res.content, 10); - if (int >= 1 && int <= players.filter(p => !p.killed).size) { - const currentVotes = votes.get(choices[int - 1]); - votes.set(ids[int - 1], { - votes: currentVotes ? currentVotes + 1 : 1, - id: ids[int - 1] - }); - reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); - return true; - } - return false; - }; - const vote = await msg.channel.awaitMessages({ - filter: voteFilter, - max: players.filter(player => !player.killed).size, - time: 60000 - }); - if (!vote.size) { - if (lastTurnTimeout) { - await msg.say('Game ended due to inactivity.'); - break; - } else { - await msg.say('Come on guys, get in the game!'); - lastTurnTimeout = true; - continue; - } - } - const kicked = players.get(votes.sort((a, b) => b.votes - a.votes).first().id); - players.get(kicked.id).killed = true; - if (kicked.id === players.find(player => player.imposter).id) { - await msg.say(`**${kicked.user.tag}** was the imposter.`); - winners.push(...players.filter(player => !player.killed).map(player => player.user.tag)); - break; - } - const amountLeft = players.filter(player => !player.killed); - await msg.say(stripIndents` - **${kicked.user.tag}** was not the imposter. - - ${amountLeft.size > 2 ? '_Next round starts in 30 seconds._' : ''} - `); - if (amountLeft.size > 2) { - await delay(30000); - } else { - winners.push(players.find(player => player.imposter).user.tag); - break; - } - if (lastTurnTimeout) lastTurnTimeout = false; - } - this.client.games.delete(msg.channel.id); - return msg.say(`Congrats, ${list(winners)}! The kill word was **${word}**.`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const awaitedPlayers = await awaitPlayers(msg, playersCount, 3, this.client.blacklist.user); + if (!awaitedPlayers) return msg.say('Game could not be started...'); + const word = words[Math.floor(Math.random() * words.length)]; + const wordRegex = new RegExp(`\\b${word}\\b`, 'i'); + const players = new Collection(); + const imposter = awaitedPlayers[Math.floor(Math.random() * awaitedPlayers.length)]; + await msg.say(oneLine` + Welcome to Imposter! In this game, you will have to figure out who the imposter is! + All you have to do is watch what other players say. There's a special word called a kill word. + Only the imposter can say it, and if anyone else does, they die! To win, figure out what the kill + word is, and try to catch the imposter saying it. As for the imposter, you know the word, try to get + everyone to say it! + `); + for (const player of awaitedPlayers) { + players.set(player, { + id: player, + user: await this.client.users.fetch(player), + killed: false, + imposter: imposter === player + }); + const newPlayer = players.get(player); + if (imposter === player) newPlayer.user.send(`You are the imposter. The kill word is ${word}.`); + else newPlayer.user.send('You are not the imposter. Be careful what you say!'); } + let lastTurnTimeout = false; + const winners = []; + while (players.filter(player => !player.killed).size > 2) { + const playersLeft = players.filter(player => !player.killed).size; + await msg.say(`There are **${playersLeft}** players left. Talk until someone says the kill word.`); + const filter = res => { + const player = players.get(res.author.id); + if (!player || player.killed || player.imposter) return false; + if (res.content && wordRegex.test(res.content)) return true; + return false; + }; + const msgs = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 600000 + }); + if (msgs.size) { + const killedMsg = msgs.first(); + try { + await killedMsg.react('🔪'); + } catch { + await killedMsg.reply('🔪'); + } + players.get(killedMsg.author.id).killed = true; + await msg.say(stripIndents` + ${killedMsg.author} has been murdered for saying the kill word! + Talk amongst yourselves, who is the imposter? Voting begins in 1 minute. + `); + } else { + await msg.say(stripIndents` + No has said the word for 10 minutes. We're voting anyway! Who looks suspicious? + Talk amongst yourselves, who is the imposter? Voting begins in 1 minute. + `); + } + await delay(60000); + const choices = players.filter(player => !player.killed); + const ids = choices.map(player => player.id); + let i = 0; + await msg.say(stripIndents` + Alright, who do you think the imposter is? You have 1 minute to vote. + + _Type the number of the player you think is the imposter._ + ${choices.map(player => { i++; return `**${i}.** ${player.user.tag}`; }).join('\n')} + `); + const votes = new Collection(); + const voteFilter = res => { + const player = players.get(res.author.id); + if (!player || player.killed) return false; + const int = Number.parseInt(res.content, 10); + if (int >= 1 && int <= players.filter(p => !p.killed).size) { + const currentVotes = votes.get(choices[int - 1]); + votes.set(ids[int - 1], { + votes: currentVotes ? currentVotes + 1 : 1, + id: ids[int - 1] + }); + reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); + return true; + } + return false; + }; + const vote = await msg.channel.awaitMessages({ + filter: voteFilter, + max: players.filter(player => !player.killed).size, + time: 60000 + }); + if (!vote.size) { + if (lastTurnTimeout) { + await msg.say('Game ended due to inactivity.'); + break; + } else { + await msg.say('Come on guys, get in the game!'); + lastTurnTimeout = true; + continue; + } + } + const kicked = players.get(votes.sort((a, b) => b.votes - a.votes).first().id); + players.get(kicked.id).killed = true; + if (kicked.id === players.find(player => player.imposter).id) { + await msg.say(`**${kicked.user.tag}** was the imposter.`); + winners.push(...players.filter(player => !player.killed).map(player => player.user.tag)); + break; + } + const amountLeft = players.filter(player => !player.killed); + await msg.say(stripIndents` + **${kicked.user.tag}** was not the imposter. + + ${amountLeft.size > 2 ? '_Next round starts in 30 seconds._' : ''} + `); + if (amountLeft.size > 2) { + await delay(30000); + } else { + winners.push(players.find(player => player.imposter).user.tag); + break; + } + if (lastTurnTimeout) lastTurnTimeout = false; + } + return msg.say(`Congrats, ${list(winners)}! The kill word was **${word}**.`); } }; diff --git a/commands/games-mp/island.js b/commands/games-mp/island.js index ea272fa1..29bed550 100644 --- a/commands/games-mp/island.js +++ b/commands/games-mp/island.js @@ -11,6 +11,7 @@ module.exports = class IslandCommand extends Command { group: 'games-mp', memberName: 'island', description: 'Who will be the final two left on the island after a series of vote-kicks?', + game: true, guildOnly: true, args: [ { @@ -25,94 +26,82 @@ module.exports = class IslandCommand extends Command { } async run(msg, { playersCount }) { - 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 { - const awaitedPlayers = await awaitPlayers(msg, playersCount, 3, this.client.blacklist.user); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); - } - let turn = 0; - const players = new Collection(); - for (const player of awaitedPlayers) { - players.set(player, { - id: player, - user: await this.client.users.fetch(player) - }); - } - let lastTurnTimeout = false; - const playersLeft = new Set(players.map(p => p.id)); - while (playersLeft.size > 2) { - ++turn; - await msg.say(stripIndents` - **Day ${turn}.** Who should be kicked off the island? - - You have **2 minutes** to make a decision before voting starts. - `); - await delay(120000); - const choices = players.filter(player => playersLeft.has(player.id)); - const ids = choices.map(player => player.id); - let i = 0; - const display = choices.map(player => { - const res = `**${i + 1}.** ${player.user.tag}`; - i++; - return res; - }); - await msg.say(stripIndents` - Alright, who do you want to kick off the island? You have 1 minute to vote. - - _Type the number of the player you want to kick._ - ${display.join('\n')} - `); - const votes = new Collection(); - const voteFilter = res => { - if (!playersLeft.has(res.author.id)) return false; - const int = Number.parseInt(res.content, 10); - if (int >= 1 && int <= playersLeft.size) { - const currentVotes = votes.get(choices[int - 1]); - votes.set(ids[int - 1], { - votes: currentVotes ? currentVotes + 1 : 1, - id: ids[int - 1] - }); - reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); - return true; - } - return false; - }; - const vote = await msg.channel.awaitMessages({ - filter: voteFilter, - max: playersLeft.size, - time: 60000 - }); - if (!vote.size) { - if (lastTurnTimeout) { - await msg.say('Game ended due to inactivity.'); - break; - } else { - await msg.say('Come on guys, get in the game!'); - lastTurnTimeout = true; - continue; - } - } - const kicked = players.get(votes.sort((a, b) => b.votes - a.votes).first().id); - playersLeft.delete(kicked.id); - await msg.say(stripIndents` - **${kicked.user.tag}** will be kicked off the island. - - ${playersLeft.size > 2 ? '_Next round starts in 30 seconds.' : ''} - `); - if (playersLeft.size > 2) await delay(30000); - else break; - if (lastTurnTimeout) lastTurnTimeout = false; - } - this.client.games.delete(msg.channel.id); - const winners = players.filter(player => playersLeft.has(player.id)); - return msg.say(`Congrats, ${winners.map(player => player.user.tag).join(' and ')}!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const awaitedPlayers = await awaitPlayers(msg, playersCount, 3, this.client.blacklist.user); + if (!awaitedPlayers) return msg.say('Game could not be started...'); + let turn = 0; + const players = new Collection(); + for (const player of awaitedPlayers) { + players.set(player, { + id: player, + user: await this.client.users.fetch(player) + }); } + let lastTurnTimeout = false; + const playersLeft = new Set(players.map(p => p.id)); + while (playersLeft.size > 2) { + ++turn; + await msg.say(stripIndents` + **Day ${turn}.** Who should be kicked off the island? + + You have **2 minutes** to make a decision before voting starts. + `); + await delay(120000); + const choices = players.filter(player => playersLeft.has(player.id)); + const ids = choices.map(player => player.id); + let i = 0; + const display = choices.map(player => { + const res = `**${i + 1}.** ${player.user.tag}`; + i++; + return res; + }); + await msg.say(stripIndents` + Alright, who do you want to kick off the island? You have 1 minute to vote. + + _Type the number of the player you want to kick._ + ${display.join('\n')} + `); + const votes = new Collection(); + const voteFilter = res => { + if (!playersLeft.has(res.author.id)) return false; + const int = Number.parseInt(res.content, 10); + if (int >= 1 && int <= playersLeft.size) { + const currentVotes = votes.get(choices[int - 1]); + votes.set(ids[int - 1], { + votes: currentVotes ? currentVotes + 1 : 1, + id: ids[int - 1] + }); + reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); + return true; + } + return false; + }; + const vote = await msg.channel.awaitMessages({ + filter: voteFilter, + max: playersLeft.size, + time: 60000 + }); + if (!vote.size) { + if (lastTurnTimeout) { + await msg.say('Game ended due to inactivity.'); + break; + } else { + await msg.say('Come on guys, get in the game!'); + lastTurnTimeout = true; + continue; + } + } + const kicked = players.get(votes.sort((a, b) => b.votes - a.votes).first().id); + playersLeft.delete(kicked.id); + await msg.say(stripIndents` + **${kicked.user.tag}** will be kicked off the island. + + ${playersLeft.size > 2 ? '_Next round starts in 30 seconds.' : ''} + `); + if (playersLeft.size > 2) await delay(30000); + else break; + if (lastTurnTimeout) lastTurnTimeout = false; + } + const winners = players.filter(player => playersLeft.has(player.id)); + return msg.say(`Congrats, ${winners.map(player => player.user.tag).join(' and ')}!`); } }; diff --git a/commands/games-mp/jenga.js b/commands/games-mp/jenga.js index 9f965ad5..06fa5fcb 100644 --- a/commands/games-mp/jenga.js +++ b/commands/games-mp/jenga.js @@ -10,6 +10,7 @@ module.exports = class JengaCommand extends Command { group: 'games-mp', memberName: 'jenga', description: 'Play a game of Jenga with another user or the AI.', + game: true, credit: [ { name: 'Jenga', @@ -29,97 +30,85 @@ module.exports = class JengaCommand extends Command { async run(msg, { opponent }) { if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 wonByFinalPiece = false; - let lastTurnTimeout = false; - let i; - while (!winner && board.length) { - const user = userTurn ? msg.author : opponent; - if (opponent.bot && !userTurn) { - i = Math.floor(Math.random() * board.length); - } else { - const text = stripIndents` - ${user}, which block do you want to remove? Type \`end\` to forfeit. - Each block you go lower on the tower, the more likely the tower falls. - `; - await msg.say(`${text}\n\n${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 j = Number.parseInt(choice, 10) - 1; - return board[j]; - }; - const turn = await msg.channel.awaitMessages({ - filter: pickFilter, - max: 1, - time: 60000 - }); - if (!turn.size) { - if (lastTurnTimeout) { - winner = 'time'; - break; - } else { - await msg.say('Sorry, time is up!'); - lastTurnTimeout = true; - userTurn = !userTurn; - continue; - } - } - const choice = turn.first().content; - const picked = Number.parseInt(choice, 10); - if (choice.toLowerCase() === 'end') { - winner = userTurn ? opponent : msg.author; + if (!opponent.bot) { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) 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 wonByFinalPiece = false; + let lastTurnTimeout = false; + let i; + while (!winner && board.length) { + const user = userTurn ? msg.author : opponent; + if (opponent.bot && !userTurn) { + i = Math.floor(Math.random() * board.length); + } else { + const text = stripIndents` + ${user}, which block do you want to remove? Type \`end\` to forfeit. + Each block you go lower on the tower, the more likely the tower falls. + `; + await msg.say(`${text}\n\n${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 j = Number.parseInt(choice, 10) - 1; + return board[j]; + }; + const turn = await msg.channel.awaitMessages({ + filter: pickFilter, + max: 1, + time: 60000 + }); + if (!turn.size) { + if (lastTurnTimeout) { + winner = 'time'; break; + } else { + await msg.say('Sorry, time is up!'); + lastTurnTimeout = true; + userTurn = !userTurn; + continue; } - i = picked - 1; } - if (board.length === 1) { - winner = userTurn ? msg.author : opponent; - wonByFinalPiece = true; - } - const fell = Math.floor(Math.random() * ((board.length + 1) - (i + 1))); - if (!fell) { + const choice = turn.first().content; + const picked = Number.parseInt(choice, 10); + if (choice.toLowerCase() === 'end') { winner = userTurn ? opponent : msg.author; break; } - await msg.say(`${opponent.bot && !userTurn ? `I pick ${i + 1}. ` : ''}Thankfully, the tower stands.`); - board.shift(); - userTurn = !userTurn; - if (lastTurnTimeout) lastTurnTimeout = false; + i = picked - 1; } - this.client.games.delete(msg.channel.id); - if (winner === 'time') return msg.say('Game ended due to inactivity.'); - let text; - if (wonByFinalPiece) { - text = opponent.bot && !userTurn - ? 'I pick up the last piece and win!' - : `${winner} picks up the last piece, winning the game!`; - } else { - text = `${opponent.bot && !userTurn ? `I pick ${i + 1}, a` : 'A'}nd the tower topples!`; + if (board.length === 1) { + winner = userTurn ? msg.author : opponent; + wonByFinalPiece = true; } - return msg.say(stripIndents` - ${text} - ${winner ? `Congrats, ${winner}!` : 'Looks like it\'s a draw...'} - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const fell = Math.floor(Math.random() * ((board.length + 1) - (i + 1))); + if (!fell) { + winner = userTurn ? opponent : msg.author; + break; + } + await msg.say(`${opponent.bot && !userTurn ? `I pick ${i + 1}. ` : ''}Thankfully, the tower stands.`); + board.shift(); + userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; } + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + let text; + if (wonByFinalPiece) { + text = opponent.bot && !userTurn + ? 'I pick up the last piece and win!' + : `${winner} picks up the last piece, winning the game!`; + } else { + text = `${opponent.bot && !userTurn ? `I pick ${i + 1}, a` : 'A'}nd the tower topples!`; + } + return msg.say(stripIndents` + ${text} + ${winner ? `Congrats, ${winner}!` : 'Looks like it\'s a draw...'} + `); } displayBoard(board) { diff --git a/commands/games-mp/lie-swatter.js b/commands/games-mp/lie-swatter.js index 5128b53d..2aa16c24 100644 --- a/commands/games-mp/lie-swatter.js +++ b/commands/games-mp/lie-swatter.js @@ -14,6 +14,7 @@ module.exports = class LieSwatterCommand extends Command { group: 'games-mp', memberName: 'lie-swatter', description: 'Players are given a fact and must quickly decide if it\'s True or a Lie.', + game: true, credit: [ { name: 'Jackbox Games', @@ -40,89 +41,80 @@ module.exports = class LieSwatterCommand extends Command { } async run(msg, { players }) { - 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 { - const awaitedPlayers = await awaitPlayers(msg, players, 1, this.client.blacklist.user); - let turn = 0; - const pts = new Collection(); - for (const player of awaitedPlayers) { - pts.set(player, { - points: 0, - id: player, - user: await this.client.users.fetch(player) - }); - } - const questions = await this.fetchQuestions(); - let lastTurnTimeout = false; - while (questions.length) { - ++turn; - const question = questions[0]; - questions.shift(); - await msg.say(stripIndents` - **${turn}. ${question.category}** - ${question.question} - - _Is it True or is it a Lie?_ - `); - const filter = res => { - if (!awaitedPlayers.includes(res.author.id)) return false; - const answer = res.content.toLowerCase(); - if (trueOptions.includes(answer) || falseOptions.includes(answer)) { - reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); - return true; - } - return false; - }; - const msgs = await msg.channel.awaitMessages({ - filter, - max: pts.size, - time: 30000 - }); - if (!msgs.size) { - await msg.say(`No answers? Well, it was ${question.answer ? 'true' : 'a lie'}.`); - if (lastTurnTimeout) { - break; - } else { - lastTurnTimeout = true; - continue; - } - } - const answers = msgs.map(res => { - let answer; - if (trueOptions.includes(res.content.toLowerCase())) answer = true; - else if (falseOptions.includes(res.content.toLowerCase())) answer = false; - return { answer, id: res.author.id }; - }); - const correct = answers.filter(answer => answer.answer === question.answer); - for (const answer of correct) { - const player = pts.get(answer.id); - if (correct[0].id === answer.id) player.points += 75; - else player.points += 50; - } - await msg.say(stripIndents` - It was... **${question.answer ? 'true' : 'a lie'}**! - - _Fastest Guess: ${correct.length ? `${pts.get(correct[0].id).user.tag} (+75 pts)` : 'No One...'}_ - - ${questions.length ? '_Next round starting in 5 seconds..._' : ''} - `); - if (lastTurnTimeout) lastTurnTimeout = false; - if (questions.length) await delay(5000); - } - this.client.games.delete(msg.channel.id); - const winner = pts.sort((a, b) => b.points - a.points).first().user; - return msg.say(stripIndents` - Congrats, ${winner}! - - __**Top 10:**__ - ${this.makeLeaderboard(pts).slice(0, 10).join('\n')} - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const awaitedPlayers = await awaitPlayers(msg, players, 1, this.client.blacklist.user); + let turn = 0; + const pts = new Collection(); + for (const player of awaitedPlayers) { + pts.set(player, { + points: 0, + id: player, + user: await this.client.users.fetch(player) + }); } + const questions = await this.fetchQuestions(); + let lastTurnTimeout = false; + while (questions.length) { + ++turn; + const question = questions[0]; + questions.shift(); + await msg.say(stripIndents` + **${turn}. ${question.category}** + ${question.question} + + _Is it True or is it a Lie?_ + `); + const filter = res => { + if (!awaitedPlayers.includes(res.author.id)) return false; + const answer = res.content.toLowerCase(); + if (trueOptions.includes(answer) || falseOptions.includes(answer)) { + reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); + return true; + } + return false; + }; + const msgs = await msg.channel.awaitMessages({ + filter, + max: pts.size, + time: 30000 + }); + if (!msgs.size) { + await msg.say(`No answers? Well, it was ${question.answer ? 'true' : 'a lie'}.`); + if (lastTurnTimeout) { + break; + } else { + lastTurnTimeout = true; + continue; + } + } + const answers = msgs.map(res => { + let answer; + if (trueOptions.includes(res.content.toLowerCase())) answer = true; + else if (falseOptions.includes(res.content.toLowerCase())) answer = false; + return { answer, id: res.author.id }; + }); + const correct = answers.filter(answer => answer.answer === question.answer); + for (const answer of correct) { + const player = pts.get(answer.id); + if (correct[0].id === answer.id) player.points += 75; + else player.points += 50; + } + await msg.say(stripIndents` + It was... **${question.answer ? 'true' : 'a lie'}**! + + _Fastest Guess: ${correct.length ? `${pts.get(correct[0].id).user.tag} (+75 pts)` : 'No One...'}_ + + ${questions.length ? '_Next round starting in 5 seconds..._' : ''} + `); + if (lastTurnTimeout) lastTurnTimeout = false; + if (questions.length) await delay(5000); + } + const winner = pts.sort((a, b) => b.points - a.points).first().user; + return msg.say(stripIndents` + Congrats, ${winner}! + + __**Top 10:**__ + ${this.makeLeaderboard(pts).slice(0, 10).join('\n')} + `); } async fetchQuestions() { diff --git a/commands/games-mp/mafia.js b/commands/games-mp/mafia.js index 0e1d46da..536d9027 100644 --- a/commands/games-mp/mafia.js +++ b/commands/games-mp/mafia.js @@ -10,13 +10,12 @@ module.exports = class MafiaCommand extends Command { group: 'games-mp', memberName: 'mafia', description: 'Who is the Mafia? Who is the detective? Will the Mafia kill them all?', - guildOnly: true + guildOnly: true, + game: true }); } async run(msg) { - 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.`); const connection = this.client.dispatchers.get(msg.guild.id); if (!connection) { const usage = this.client.registry.commands.get('join').usage(); @@ -31,61 +30,54 @@ module.exports = class MafiaCommand extends Command { return msg.reply('Please have at least 5 users in this voice channel before starting.'); } const game = new Game(this.client, msg.channel, connection); - this.client.games.set(msg.channel.id, game); - try { - await game.generate(connection.channel.members.filter(m => !m.user.bot).map(m => m.user)); - await game.playAudio('init'); - await game.playAudio('rule-ask'); - await msg.say('Type `yes` to hear a rule explanation.'); - const rules = await verify(msg.channel, msg.author); - if (rules) await game.playAudio('rules'); - while (!game.shouldEnd) { - let killed = null; - await game.playAudio(`night-${game.turn}`); - await game.playAudio('mafia'); - const mafia = game.players.filter(p => p.role === 'mafia'); - const choices = await Promise.all(mafia.map(player => player.dmRound())); - const randomizer = choices.filter(c => c !== null); - if (randomizer.length) killed = game.players.get(randomizer[Math.floor(Math.random() * randomizer.length)]); - await game.playAudio('mafia-decision-made'); - const detective = game.players.find(p => p.role === 'detective'); - if (detective) { - await game.playAudio('detective'); - await detective.dmRound(); - await game.playAudio('detective-decision-made'); - } - await game.playAudio(`day-${game.turn}`); - if (killed) { - const story = Math.floor(Math.random() * storyCount) + 1; - await game.playAudio(`story-${story}`); - await game.playAudio('reveal-deceased'); - await msg.say(`Deceased: **${killed}**`); - game.players.delete(killed.id); - } else { - await game.playAudio('no-deceased'); - } - await game.playAudio('vote'); - const playersArr = Array.from(game.players.values()); - const votes = await game.getVotes(playersArr); - if (!votes) { - await game.playAudio('no-votes'); - continue; - } - const hanged = game.getHanged(votes, playersArr); - await game.playAudio('hanged'); - await msg.say(`Hanged: **${hanged.user}**`); - game.players.delete(hanged.id); - ++game.turn; + await game.generate(connection.channel.members.filter(m => !m.user.bot).map(m => m.user)); + await game.playAudio('init'); + await game.playAudio('rule-ask'); + await msg.say('Type `yes` to hear a rule explanation.'); + const rules = await verify(msg.channel, msg.author); + if (rules) await game.playAudio('rules'); + while (!game.shouldEnd) { + let killed = null; + await game.playAudio(`night-${game.turn}`); + await game.playAudio('mafia'); + const mafia = game.players.filter(p => p.role === 'mafia'); + const choices = await Promise.all(mafia.map(player => player.dmRound())); + const randomizer = choices.filter(c => c !== null); + if (randomizer.length) killed = game.players.get(randomizer[Math.floor(Math.random() * randomizer.length)]); + await game.playAudio('mafia-decision-made'); + const detective = game.players.find(p => p.role === 'detective'); + if (detective) { + await game.playAudio('detective'); + await detective.dmRound(); + await game.playAudio('detective-decision-made'); } - const mafia = game.players.find(p => p.role === 'mafia'); - if (mafia) await game.playAudio('mafia-wins'); - else await game.playAudio('mafia-loses'); - await game.playAudio('credits'); - this.client.games.delete(msg.channel.id); - return null; - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + await game.playAudio(`day-${game.turn}`); + if (killed) { + const story = Math.floor(Math.random() * storyCount) + 1; + await game.playAudio(`story-${story}`); + await game.playAudio('reveal-deceased'); + await msg.say(`Deceased: **${killed}**`); + game.players.delete(killed.id); + } else { + await game.playAudio('no-deceased'); + } + await game.playAudio('vote'); + const playersArr = Array.from(game.players.values()); + const votes = await game.getVotes(playersArr); + if (!votes) { + await game.playAudio('no-votes'); + continue; + } + const hanged = game.getHanged(votes, playersArr); + await game.playAudio('hanged'); + await msg.say(`Hanged: **${hanged.user}**`); + game.players.delete(hanged.id); + ++game.turn; } + const mafia = game.players.find(p => p.role === 'mafia'); + if (mafia) await game.playAudio('mafia-wins'); + else await game.playAudio('mafia-loses'); + await game.playAudio('credits'); + return null; } }; diff --git a/commands/games-mp/nim.js b/commands/games-mp/nim.js index e738940f..404441f1 100644 --- a/commands/games-mp/nim.js +++ b/commands/games-mp/nim.js @@ -11,6 +11,7 @@ module.exports = class NimCommand extends Command { group: 'games-mp', memberName: 'nim', description: 'Play a game of nim with another user or the AI.', + game: true, args: [ { key: 'opponent', @@ -30,52 +31,85 @@ module.exports = class NimCommand extends Command { async run(msg, { opponent, rows }) { if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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...'); + if (!opponent.bot) { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + } + const board = this.generateBoard(rows); + let userTurn = true; + let winner = null; + let lastTurnTimeout = false; + let firstTurn = true; + const objectEmoji = emoji[Math.floor(Math.random() * emoji.length)]; + while (!winner) { + const user = userTurn ? msg.author : opponent; + if (!userTurn && opponent.bot) { + const turn = this.computerTurn(board); + await msg.say(`For my turn, I remove **${turn[1]}** ${objectEmoji} from **row ${turn[0] + 1}**.`); + } else { + await msg.say(stripIndents` + ${user}, from which row do you want to remove from? Type \`end\` to forfeit. + After this step, you will decide how many ${objectEmoji} to remove from that row. + + ${this.displayBoard(board, objectEmoji)} + + ${firstTurn ? '_In Nim, you win by forcing the opponent to take the last object._' : ''} + `); + 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] && board[i] > 0; + }; + const turn = await msg.channel.awaitMessages({ + filter: pickFilter, + max: 1, + time: 60000 + }); + if (!turn.size) { + if (lastTurnTimeout) { + winner = 'time'; + break; + } else { + await msg.say('Sorry, time is up!'); + lastTurnTimeout = true; + userTurn = !userTurn; + continue; + } } - } - const board = this.generateBoard(rows); - let userTurn = true; - let winner = null; - let lastTurnTimeout = false; - let firstTurn = true; - const objectEmoji = emoji[Math.floor(Math.random() * emoji.length)]; - while (!winner) { - const user = userTurn ? msg.author : opponent; - if (!userTurn && opponent.bot) { - const turn = this.computerTurn(board); - await msg.say(`For my turn, I remove **${turn[1]}** ${objectEmoji} from **row ${turn[0] + 1}**.`); + const choice = turn.first().content; + const picked = Number.parseInt(choice, 10); + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + const row = board[picked - 1]; + let rowPicked; + if (row === 1) { + rowPicked = 1; } else { await msg.say(stripIndents` - ${user}, from which row do you want to remove from? Type \`end\` to forfeit. - After this step, you will decide how many ${objectEmoji} to remove from that row. + ${user}, how many ${objectEmoji} do you want to take from row ${picked}? Type \`end\` to forfeit. + If you want to go back, type \`back\`. - ${this.displayBoard(board, objectEmoji)} - - ${firstTurn ? '_In Nim, you win by forcing the opponent to take the last object._' : ''} + ${nums[picked - 1]}${objectEmoji.repeat(row)} `); - const pickFilter = res => { + const rowFilter = 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] && board[i] > 0; + const chosen = res.content; + if (chosen.toLowerCase() === 'end' || chosen.toLowerCase() === 'back') return true; + const i = Number.parseInt(chosen, 10); + return i <= row && i > 0; }; - const turn = await msg.channel.awaitMessages({ - filter: pickFilter, + const rowTurn = await msg.channel.awaitMessages({ + filter: rowFilter, max: 1, time: 60000 }); - if (!turn.size) { - if (lastTurnTimeout) { + if (!rowTurn.size) { + if (lastTurnTimeout) { // eslint-disable-line max-depth winner = 'time'; break; } else { @@ -85,71 +119,26 @@ module.exports = class NimCommand extends Command { continue; } } - const choice = turn.first().content; - const picked = Number.parseInt(choice, 10); - if (choice.toLowerCase() === 'end') { + const rowChoice = rowTurn.first().content; + rowPicked = Number.parseInt(rowChoice, 10); + if (rowChoice.toLowerCase() === 'end') { winner = userTurn ? opponent : msg.author; break; } - const row = board[picked - 1]; - let rowPicked; - if (row === 1) { - rowPicked = 1; - } else { - await msg.say(stripIndents` - ${user}, how many ${objectEmoji} do you want to take from row ${picked}? Type \`end\` to forfeit. - If you want to go back, type \`back\`. - - ${nums[picked - 1]}${objectEmoji.repeat(row)} - `); - const rowFilter = res => { - if (res.author.id !== user.id) return false; - const chosen = res.content; - if (chosen.toLowerCase() === 'end' || chosen.toLowerCase() === 'back') return true; - const i = Number.parseInt(chosen, 10); - return i <= row && i > 0; - }; - const rowTurn = await msg.channel.awaitMessages({ - filter: rowFilter, - max: 1, - time: 60000 - }); - if (!rowTurn.size) { - if (lastTurnTimeout) { // eslint-disable-line max-depth - winner = 'time'; - break; - } else { - await msg.say('Sorry, time is up!'); - lastTurnTimeout = true; - userTurn = !userTurn; - continue; - } - } - const rowChoice = rowTurn.first().content; - rowPicked = Number.parseInt(rowChoice, 10); - if (rowChoice.toLowerCase() === 'end') { - winner = userTurn ? opponent : msg.author; - break; - } - if (rowChoice.toLowerCase() === 'back') continue; - } - board[picked - 1] -= rowPicked; + if (rowChoice.toLowerCase() === 'back') continue; } - if (!userTurn && firstTurn) firstTurn = false; - if (!board.some(r => r !== 0)) { - winner = userTurn ? opponent : msg.author; - break; - } - userTurn = !userTurn; - if (lastTurnTimeout) lastTurnTimeout = false; + board[picked - 1] -= rowPicked; } - this.client.games.delete(msg.channel.id); - if (winner === 'time') return msg.say('Game ended due to inactivity.'); - return msg.say(`Congrats, ${winner}! You forced your opponent to pick the final object!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + if (!userTurn && firstTurn) firstTurn = false; + if (!board.some(r => r !== 0)) { + winner = userTurn ? opponent : msg.author; + break; + } + userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; } + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(`Congrats, ${winner}! You forced your opponent to pick the final object!`); } displayBoard(board, objectEmoji) { diff --git a/commands/games-mp/obstruction.js b/commands/games-mp/obstruction.js index c402c8b0..a408bece 100644 --- a/commands/games-mp/obstruction.js +++ b/commands/games-mp/obstruction.js @@ -17,6 +17,7 @@ module.exports = class ObstructionCommand extends Command { memberName: 'obstruction', description: 'Play a game of Obstruction with another user.', guildOnly: true, + game: true, args: [ { key: 'opponent', @@ -37,97 +38,85 @@ module.exports = class ObstructionCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - 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 = this.generateBoard(size); - let userTurn = true; - let winner = null; - let lastTurnTimeout = false; - while (!winner) { - const user = userTurn ? msg.author : opponent; - await msg.say(stripIndents` - ${user}, at what coordinates do you want to place your piece (ex. 1,1)? Type \`end\` to forfeit. - Every piece you place will obstruct the 8 pieces around it. - - ${this.displayBoard(board)} - `); - const possibleMoves = this.possibleMoves(board); - const turnFilter = res => { - if (res.author.id !== user.id) return false; - const pick = res.content; - if (pick.toLowerCase() === 'end') return true; - const coordPicked = pick.match(turnRegex); - if (!coordPicked) return false; - const x = Number.parseInt(coordPicked[1], 10); - const y = Number.parseInt(coordPicked[2], 10); - if (x > size || y > size || x < 1 || y < 1) return false; - if (!possibleMoves.includes(`${x - 1},${y - 1}`)) return false; - return true; - }; - const turn = await msg.channel.awaitMessages({ - filter: turnFilter, - max: 1, - time: 60000 - }); - if (!turn.size) { - await msg.say('Sorry, time is up!'); - if (lastTurnTimeout) { - winner = 'time'; - break; - } else { - lastTurnTimeout = true; - userTurn = !userTurn; - continue; - } - } - const choice = turn.first().content; - if (choice.toLowerCase() === 'end') { - winner = userTurn ? opponent : msg.author; - break; - } - const matched = choice.match(turnRegex); - const x = Number.parseInt(matched[1], 10) - 1; - const y = Number.parseInt(matched[2], 10) - 1; - board[y][x] = userTurn ? 'X' : 'O'; - if (board[y - 1]) { - if (board[y - 1][x]) board[y - 1][x] = 'B'; - if (board[y - 1][x - 1]) board[y - 1][x - 1] = 'B'; - if (board[y - 1][x + 1]) board[y - 1][x + 1] = 'B'; - } - if (board[y + 1]) { - if (board[y + 1][x]) board[y + 1][x] = 'B'; - if (board[y + 1][x + 1]) board[y + 1][x + 1] = 'B'; - if (board[y + 1][x - 1]) board[y + 1][x - 1] = 'B'; - } - if (board[y][x - 1]) board[y][x - 1] = 'B'; - if (board[y][x + 1]) board[y][x + 1] = 'B'; - userTurn = !userTurn; - if (lastTurnTimeout) lastTurnTimeout = false; - const oppoPossible = this.possibleMoves(board); - if (!oppoPossible.length) { - winner = userTurn ? opponent : msg.author; - break; - } - } - this.client.games.delete(msg.channel.id); - if (winner === 'time') return msg.say('Game ended due to inactivity.'); - return msg.say(stripIndents` - Congrats, ${winner}! Your opponent has no possible moves left! + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + const board = this.generateBoard(size); + let userTurn = true; + let winner = null; + let lastTurnTimeout = false; + while (!winner) { + const user = userTurn ? msg.author : opponent; + await msg.say(stripIndents` + ${user}, at what coordinates do you want to place your piece (ex. 1,1)? Type \`end\` to forfeit. + Every piece you place will obstruct the 8 pieces around it. ${this.displayBoard(board)} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const possibleMoves = this.possibleMoves(board); + const turnFilter = res => { + if (res.author.id !== user.id) return false; + const pick = res.content; + if (pick.toLowerCase() === 'end') return true; + const coordPicked = pick.match(turnRegex); + if (!coordPicked) return false; + const x = Number.parseInt(coordPicked[1], 10); + const y = Number.parseInt(coordPicked[2], 10); + if (x > size || y > size || x < 1 || y < 1) return false; + if (!possibleMoves.includes(`${x - 1},${y - 1}`)) return false; + return true; + }; + const turn = await msg.channel.awaitMessages({ + filter: turnFilter, + max: 1, + time: 60000 + }); + if (!turn.size) { + await msg.say('Sorry, time is up!'); + if (lastTurnTimeout) { + winner = 'time'; + break; + } else { + lastTurnTimeout = true; + userTurn = !userTurn; + continue; + } + } + const choice = turn.first().content; + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + const matched = choice.match(turnRegex); + const x = Number.parseInt(matched[1], 10) - 1; + const y = Number.parseInt(matched[2], 10) - 1; + board[y][x] = userTurn ? 'X' : 'O'; + if (board[y - 1]) { + if (board[y - 1][x]) board[y - 1][x] = 'B'; + if (board[y - 1][x - 1]) board[y - 1][x - 1] = 'B'; + if (board[y - 1][x + 1]) board[y - 1][x + 1] = 'B'; + } + if (board[y + 1]) { + if (board[y + 1][x]) board[y + 1][x] = 'B'; + if (board[y + 1][x + 1]) board[y + 1][x + 1] = 'B'; + if (board[y + 1][x - 1]) board[y + 1][x - 1] = 'B'; + } + if (board[y][x - 1]) board[y][x - 1] = 'B'; + if (board[y][x + 1]) board[y][x + 1] = 'B'; + userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; + const oppoPossible = this.possibleMoves(board); + if (!oppoPossible.length) { + winner = userTurn ? opponent : msg.author; + break; + } } + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(stripIndents` + Congrats, ${winner}! Your opponent has no possible moves left! + + ${this.displayBoard(board)} + `); } possibleMoves(board) { diff --git a/commands/games-mp/pick-a-number.js b/commands/games-mp/pick-a-number.js index f9528116..62fca32f 100644 --- a/commands/games-mp/pick-a-number.js +++ b/commands/games-mp/pick-a-number.js @@ -10,6 +10,7 @@ module.exports = class PickANumberCommand extends Command { group: 'games-mp', memberName: 'pick-a-number', description: 'Two players pick a number between 1 and 10. Whoever\'s closer wins.', + game: true, args: [ { key: 'opponent', @@ -22,66 +23,48 @@ module.exports = class PickANumberCommand extends Command { async run(msg, { opponent }) { if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - const clientOpp = opponent.id === this.client.user.id; - 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...'); - } - } - await msg.say(`${msg.author}, pick a number from 1 to 10.`); - let userTurn = true; - let player1Pick = null; - const filter = res => { - if (res.author.id !== (userTurn ? msg.author.id : opponent.id)) return false; - const num = Number.parseInt(res.content, 10); - if (!userTurn && num === player1Pick) return false; - return num && nums.includes(num); - }; - const player1 = await msg.channel.awaitMessages({ + const clientOpp = opponent.id === this.client.user.id; + if (!opponent.bot) { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + } + await msg.say(`${msg.author}, pick a number from 1 to 10.`); + let userTurn = true; + let player1Pick = null; + const filter = res => { + if (res.author.id !== (userTurn ? msg.author.id : opponent.id)) return false; + const num = Number.parseInt(res.content, 10); + if (!userTurn && num === player1Pick) return false; + return num && nums.includes(num); + }; + const player1 = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (!player1.size) return msg.say('I guess you didn\'t want to play after all...'); + player1Pick = Number.parseInt(player1.first().content, 10); + let player2Pick; + if (opponent.bot) { + const valid = nums.filter(num => num !== player1Pick); + player2Pick = valid[Math.floor(Math.random() * valid.length)]; + await msg.say(`Okay, ${clientOpp ? 'I' : `${opponent}`} pick${clientOpp ? '' : 's'} ${player2Pick}!`); + } else { + userTurn = false; + await msg.say(`${opponent}, pick a number from 1 to 10, except ${player1Pick}.`); + const player2 = await msg.channel.awaitMessages({ filter, max: 1, time: 30000 }); - if (!player1.size) { - this.client.games.delete(msg.channel.id); - return msg.say('I guess you didn\'t want to play after all...'); - } - player1Pick = Number.parseInt(player1.first().content, 10); - let player2Pick; - if (opponent.bot) { - const valid = nums.filter(num => num !== player1Pick); - player2Pick = valid[Math.floor(Math.random() * valid.length)]; - await msg.say(`Okay, ${clientOpp ? 'I' : `${opponent}`} pick${clientOpp ? '' : 's'} ${player2Pick}!`); - } else { - userTurn = false; - await msg.say(`${opponent}, pick a number from 1 to 10, except ${player1Pick}.`); - const player2 = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (!player2.size) { - this.client.games.delete(msg.channel.id); - return msg.say('I guess you didn\'t want to play after all...'); - } - player2Pick = Number.parseInt(player2.first().content, 10); - } - const num = Math.floor(Math.random() * 10) + 1; - const winNum = [player1Pick, player2Pick].sort((a, b) => Math.abs(num - a) - Math.abs(num - b))[0]; - const winner = winNum === player1Pick ? msg.author : opponent; - const clientWin = clientOpp && this.client.user.id === winner.id; - this.client.games.delete(msg.channel.id); - return msg.say(`The number was **${num}**! ${clientWin ? 'I' : winner} win${clientWin ? '' : 's'}!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + if (!player2.size) return msg.say('I guess you didn\'t want to play after all...'); + player2Pick = Number.parseInt(player2.first().content, 10); } + const num = Math.floor(Math.random() * 10) + 1; + const winNum = [player1Pick, player2Pick].sort((a, b) => Math.abs(num - a) - Math.abs(num - b))[0]; + const winner = winNum === player1Pick ? msg.author : opponent; + const clientWin = clientOpp && this.client.user.id === winner.id; + return msg.say(`The number was **${num}**! ${clientWin ? 'I' : winner} win${clientWin ? '' : 's'}!`); } }; diff --git a/commands/games-mp/poker.js b/commands/games-mp/poker.js index cc13f846..a995999f 100644 --- a/commands/games-mp/poker.js +++ b/commands/games-mp/poker.js @@ -19,6 +19,7 @@ module.exports = class PokerCommand extends Command { memberName: 'poker', description: `Play poker with up to ${max - 1} other users.`, guildOnly: true, + game: true, args: [ { key: 'playersCount', @@ -31,180 +32,159 @@ module.exports = class PokerCommand extends Command { } async run(msg, { playersCount }) { - 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, - data: { - deck: new Deck(), - players: new Collection(), - turnData: { - pot: 0, - currentBet: 0, - highestBetter: null - } + const awaitedPlayers = await awaitPlayers(msg, playersCount, min, this.client.blacklist.user); + if (!awaitedPlayers) return msg.say('Game could not be started...'); + const players = new Collection(); + const deck = new Deck(); + const turnData = { pot: 0, currentBet: 0, highestBetter: 0 }; + for (const player of awaitedPlayers) { + players.set(player, { + money: 2000, + id: player, + hand: [], + user: await this.client.users.fetch(player), + currentBet: 0, + hasGoneOnce: false, + strikes: 0, + isAllIn: false + }); + } + let winner = null; + const rotation = players.map(p => p.id); + while (!winner) { + for (const player of rotation) { + if (players.has(player)) continue; + removeFromArray(rotation, player); } - }); - try { - const awaitedPlayers = await awaitPlayers(msg, playersCount, min, this.client.blacklist.user); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); - } - const { players, deck, turnData } = this.client.games.get(msg.channel.id).data; - for (const player of awaitedPlayers) { - players.set(player, { - money: 2000, - id: player, - hand: [], - user: await this.client.users.fetch(player), - currentBet: 0, - hasGoneOnce: false, - strikes: 0, - isAllIn: false - }); - } - let winner = null; - const rotation = players.map(p => p.id); - while (!winner) { - for (const player of rotation) { - if (players.has(player)) continue; - removeFromArray(rotation, player); - } - const bigBlind = players.get(rotation[1]); - bigBlind.money -= bigBlindAmount; - bigBlind.currentBet += bigBlindAmount; - const smallBlind = players.get(rotation[2] || rotation[0]); - smallBlind.money -= smallBlindAmount; - smallBlind.currentBet += smallBlindAmount; - rotation.push(rotation[0]); - rotation.shift(); - const folded = []; - await msg.say('Dealing player hands...'); - for (const player of players.values()) { - player.hand.push(...deck.draw(2)); - try { - await player.user.send(stripIndents` - _Back to ${msg.channel}._ + const bigBlind = players.get(rotation[1]); + bigBlind.money -= bigBlindAmount; + bigBlind.currentBet += bigBlindAmount; + const smallBlind = players.get(rotation[2] || rotation[0]); + smallBlind.money -= smallBlindAmount; + smallBlind.currentBet += smallBlindAmount; + rotation.push(rotation[0]); + rotation.shift(); + const folded = []; + await msg.say('Dealing player hands...'); + for (const player of players.values()) { + player.hand.push(...deck.draw(2)); + try { + await player.user.send(stripIndents` + _Back to ${msg.channel}._ - **Your Poker Hand:** - ${player.hand.map(c => c.textDisplay).join('\n')} + **Your Poker Hand:** + ${player.hand.map(c => c.textDisplay).join('\n')} - **Money:** $${formatNumber(player.money)} - ${bigBlind.id === player.id ? '_You are the big blind._' : ''} - ${smallBlind.id === player.id ? '_You are the small blind._' : ''} - `); - } catch { - await msg.say(`${player.user}, I couldn't send your hand! Turn on DMs!`); - } - } - turnData.pot = bigBlindAmount + smallBlindAmount; - turnData.currentBet = bigBlindAmount; - turnData.highestBetter = bigBlind; - let keepGoing = await this.gameRound(msg, players, deck, folded, turnData, bigBlind, smallBlind); - if (!keepGoing) { - if (players.size < 2) { - winner = players.first(); - break; - } - continue; - } - const dealerHand = deck.draw(3); - await msg.say(stripIndents` - **Dealer Hand:** - ${dealerHand.map(card => card.textDisplay).join('\n')} - - _Next betting round begins in 10 seconds._ - `); - await delay(10000); - keepGoing = await this.gameRound(msg, players, deck, folded, turnData, bigBlind, smallBlind); - if (!keepGoing) { - if (players.size < 2) { - winner = players.first(); - break; - } - continue; - } - dealerHand.push(deck.draw()); - await msg.say(stripIndents` - **Dealer Hand:** - ${dealerHand.map(card => card.textDisplay).join('\n')} - - _Next betting round begins in 10 seconds._ - `); - await delay(10000); - keepGoing = await this.gameRound(msg, players, deck, folded, turnData, bigBlind, smallBlind); - if (!keepGoing) { - if (players.size < 2) { - winner = players.first(); - break; - } - continue; - } - dealerHand.push(deck.draw()); - await msg.say(stripIndents` - **Dealer Hand:** - ${dealerHand.map(card => card.textDisplay).join('\n')} - - _Next betting round begins in 10 seconds._ - `); - await delay(10000); - keepGoing = await this.gameRound(msg, players, deck, folded, turnData, bigBlind, smallBlind); - if (!keepGoing) { - if (players.size < 2) { - winner = players.first(); - break; - } - continue; - } - const solved = []; - for (const playerID of rotation) { - if (folded.includes(playerID)) continue; - const player = players.get(playerID); - const solvedHand = Hand.solve([ - ...player.hand.map(card => card.pokersolverKey), - ...dealerHand.map(card => card.pokersolverKey) - ]); - solvedHand.user = player; - solved.push(solvedHand); - } - const winners = Hand.winners(solved); - if (winners.length > 1) { - await msg.say(stripIndents` - The pot will be split between ${list(winners.map(w => `**${w.user.user}**`))}. - ${winners.map(winner.descr).join(', ')} - - __**Results**__ - ${solved.map(solve => `${solve.user.user.tag}: ${solve.descr}`).join('\n')} - - _Next game starting in 15 seconds._ + **Money:** $${formatNumber(player.money)} + ${bigBlind.id === player.id ? '_You are the big blind._' : ''} + ${smallBlind.id === player.id ? '_You are the small blind._' : ''} `); - const splitPot = turnData.pot / winners.length; - for (const win of winners) win.user.money += splitPot; - } else { - await msg.say(stripIndents` - ${winners[0].user.user} takes the pot, with **${winners[0].descr}**. - - __**Results**__ - ${solved.map(solve => `${solve.user.user.tag}: ${solve.descr}`).join('\n')} - - _Next game starting in 15 seconds._ - `); - winners[0].user.money += turnData.pot; + } catch { + await msg.say(`${player.user}, I couldn't send your hand! Turn on DMs!`); } - await this.resetGame(msg, players, deck); + } + turnData.pot = bigBlindAmount + smallBlindAmount; + turnData.currentBet = bigBlindAmount; + turnData.highestBetter = bigBlind; + let keepGoing = await this.gameRound(msg, players, deck, folded, turnData, bigBlind, smallBlind); + if (!keepGoing) { if (players.size < 2) { winner = players.first(); break; } - await delay(15000); + continue; } - this.client.games.delete(msg.channel.id); - return msg.say(`Congrats, ${winner.user}!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const dealerHand = deck.draw(3); + await msg.say(stripIndents` + **Dealer Hand:** + ${dealerHand.map(card => card.textDisplay).join('\n')} + + _Next betting round begins in 10 seconds._ + `); + await delay(10000); + keepGoing = await this.gameRound(msg, players, deck, folded, turnData, bigBlind, smallBlind); + if (!keepGoing) { + if (players.size < 2) { + winner = players.first(); + break; + } + continue; + } + dealerHand.push(deck.draw()); + await msg.say(stripIndents` + **Dealer Hand:** + ${dealerHand.map(card => card.textDisplay).join('\n')} + + _Next betting round begins in 10 seconds._ + `); + await delay(10000); + keepGoing = await this.gameRound(msg, players, deck, folded, turnData, bigBlind, smallBlind); + if (!keepGoing) { + if (players.size < 2) { + winner = players.first(); + break; + } + continue; + } + dealerHand.push(deck.draw()); + await msg.say(stripIndents` + **Dealer Hand:** + ${dealerHand.map(card => card.textDisplay).join('\n')} + + _Next betting round begins in 10 seconds._ + `); + await delay(10000); + keepGoing = await this.gameRound(msg, players, deck, folded, turnData, bigBlind, smallBlind); + if (!keepGoing) { + if (players.size < 2) { + winner = players.first(); + break; + } + continue; + } + const solved = []; + for (const playerID of rotation) { + if (folded.includes(playerID)) continue; + const player = players.get(playerID); + const solvedHand = Hand.solve([ + ...player.hand.map(card => card.pokersolverKey), + ...dealerHand.map(card => card.pokersolverKey) + ]); + solvedHand.user = player; + solved.push(solvedHand); + } + const winners = Hand.winners(solved); + if (winners.length > 1) { + await msg.say(stripIndents` + The pot will be split between ${list(winners.map(w => `**${w.user.user}**`))}. + ${winners.map(winner.descr).join(', ')} + + __**Results**__ + ${solved.map(solve => `${solve.user.user.tag}: ${solve.descr}`).join('\n')} + + _Next game starting in 15 seconds._ + `); + const splitPot = turnData.pot / winners.length; + for (const win of winners) win.user.money += splitPot; + } else { + await msg.say(stripIndents` + ${winners[0].user.user} takes the pot, with **${winners[0].descr}**. + + __**Results**__ + ${solved.map(solve => `${solve.user.user.tag}: ${solve.descr}`).join('\n')} + + _Next game starting in 15 seconds._ + `); + winners[0].user.money += turnData.pot; + } + await this.resetGame(msg, players, deck); + if (players.size < 2) { + winner = players.first(); + break; + } + await delay(15000); } + return msg.say(`Congrats, ${winner.user}!`); } determineActions(turnPlayer, currentBet, playerAllIn) { diff --git a/commands/games-mp/quiz-duel.js b/commands/games-mp/quiz-duel.js index c88b49fc..a18367ec 100644 --- a/commands/games-mp/quiz-duel.js +++ b/commands/games-mp/quiz-duel.js @@ -14,6 +14,7 @@ module.exports = class QuizDuelCommand extends Command { group: 'games-mp', memberName: 'quiz-duel', description: 'Answer a series of quiz questions against other opponents.', + game: true, credit: [ { name: 'Open Trivia DB', @@ -34,89 +35,80 @@ module.exports = class QuizDuelCommand extends Command { } async run(msg, { players }) { - 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 { - const awaitedPlayers = await awaitPlayers(msg, players, 1, this.client.blacklist.user); - let turn = 0; - const pts = new Collection(); - for (const player of awaitedPlayers) { - pts.set(player, { - points: 0, - id: player, - user: await this.client.users.fetch(player) - }); - } - const questions = await this.fetchQuestions(); - let lastTurnTimeout = false; - while (questions.length) { - ++turn; - const question = questions[0]; - questions.shift(); - await msg.say(stripIndents` - **${turn}. ${question.category}** - ${question.question} - ${question.answers.map((answer, i) => `**${choices[i]}.** ${answer}`).join('\n')} - `); - const filter = res => { - if (!awaitedPlayers.includes(res.author.id)) return false; - const answer = res.content.toUpperCase(); - if (choices.includes(answer)) { - reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); - return true; - } - return false; - }; - const msgs = await msg.channel.awaitMessages({ - filter, - max: pts.size, - time: 30000 - }); - if (!msgs.size) { - await msg.say(`No answers? Well, it was **${question.correct}**.`); - if (lastTurnTimeout) { - break; - } else { - lastTurnTimeout = true; - continue; - } - } - const answers = msgs.map(res => { - const choice = choices.indexOf(res.content.toUpperCase()); - return { - answer: question.answers[choice], - id: res.author.id - }; - }); - const correct = answers.filter(answer => answer.answer === question.correct); - for (const answer of correct) { - const player = pts.get(answer.id); - if (correct[0].id === answer.id) player.points += 75; - else player.points += 50; - } - await msg.say(stripIndents` - It was... **${question.correct}**! - - _Fastest Guess: ${correct.length ? `${pts.get(correct[0].id).user.tag} (+75 pts)` : 'No One...'}_ - - ${questions.length ? '_Next round starting in 5 seconds..._' : ''} - `); - if (lastTurnTimeout) lastTurnTimeout = false; - if (questions.length) await delay(5000); - } - this.client.games.delete(msg.channel.id); - const winner = pts.sort((a, b) => b.points - a.points).first().user; - return msg.say(stripIndents` - Congrats, ${winner}! - - __**Top 10:**__ - ${this.makeLeaderboard(pts).slice(0, 10).join('\n')} - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const awaitedPlayers = await awaitPlayers(msg, players, 1, this.client.blacklist.user); + let turn = 0; + const pts = new Collection(); + for (const player of awaitedPlayers) { + pts.set(player, { + points: 0, + id: player, + user: await this.client.users.fetch(player) + }); } + const questions = await this.fetchQuestions(); + let lastTurnTimeout = false; + while (questions.length) { + ++turn; + const question = questions[0]; + questions.shift(); + await msg.say(stripIndents` + **${turn}. ${question.category}** + ${question.question} + ${question.answers.map((answer, i) => `**${choices[i]}.** ${answer}`).join('\n')} + `); + const filter = res => { + if (!awaitedPlayers.includes(res.author.id)) return false; + const answer = res.content.toUpperCase(); + if (choices.includes(answer)) { + reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); + return true; + } + return false; + }; + const msgs = await msg.channel.awaitMessages({ + filter, + max: pts.size, + time: 30000 + }); + if (!msgs.size) { + await msg.say(`No answers? Well, it was **${question.correct}**.`); + if (lastTurnTimeout) { + break; + } else { + lastTurnTimeout = true; + continue; + } + } + const answers = msgs.map(res => { + const choice = choices.indexOf(res.content.toUpperCase()); + return { + answer: question.answers[choice], + id: res.author.id + }; + }); + const correct = answers.filter(answer => answer.answer === question.correct); + for (const answer of correct) { + const player = pts.get(answer.id); + if (correct[0].id === answer.id) player.points += 75; + else player.points += 50; + } + await msg.say(stripIndents` + It was... **${question.correct}**! + + _Fastest Guess: ${correct.length ? `${pts.get(correct[0].id).user.tag} (+75 pts)` : 'No One...'}_ + + ${questions.length ? '_Next round starting in 5 seconds..._' : ''} + `); + if (lastTurnTimeout) lastTurnTimeout = false; + if (questions.length) await delay(5000); + } + const winner = pts.sort((a, b) => b.points - a.points).first().user; + return msg.say(stripIndents` + Congrats, ${winner}! + + __**Top 10:**__ + ${this.makeLeaderboard(pts).slice(0, 10).join('\n')} + `); } async fetchQuestions() { diff --git a/commands/games-mp/russian-roulette.js b/commands/games-mp/russian-roulette.js index f8c6d448..cfe17fa6 100644 --- a/commands/games-mp/russian-roulette.js +++ b/commands/games-mp/russian-roulette.js @@ -11,6 +11,7 @@ module.exports = class RussianRouletteCommand extends Command { group: 'games-mp', memberName: 'russian-roulette', description: 'Who will pull the trigger and die first?', + game: true, args: [ { key: 'playersCount', @@ -23,87 +24,73 @@ module.exports = class RussianRouletteCommand extends Command { } async run(msg, { playersCount }) { - 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 { - const awaitedPlayers = await awaitPlayers(msg, playersCount, 1, this.client.blacklist.user); - if (!awaitedPlayers) { - this.client.games.delete(msg.channel.id); - return msg.say('Game could not be started...'); - } - const players = new Collection(); - for (const player of awaitedPlayers) { - players.set(player, { - id: player, - user: await this.client.users.fetch(player) - }); - } - players.set(this.client.user.id, { - id: this.client.user.id, - user: this.client.user + const awaitedPlayers = await awaitPlayers(msg, playersCount, 1, this.client.blacklist.user); + if (!awaitedPlayers) return msg.say('Game could not be started...'); + const players = new Collection(); + for (const player of awaitedPlayers) { + players.set(player, { + id: player, + user: await this.client.users.fetch(player) }); - const turn = shuffle(players.map(player => player.id)); - const gun = shuffle([true, false, false, false, false, false, false, false]); - let round = 0; - let loser = null; - let winner = null; - const wallOfShame = []; - while (!loser && !winner) { - const player = players.get(turn[0]); - turn.push(turn[0]); - turn.shift(); - if (gun[round]) { - await msg.say(`**${player.user.tag}** pulls the trigger... **And dies!**`); - loser = player; - } else { - const nextUp = players.get(turn[0]).user; - await msg.say(stripIndents` - **${player.user.tag}** pulls the trigger... **And lives...** - ${nextUp.bot ? '' : `Will you take the gun, ${nextUp}?`} - `); - if (!nextUp.bot) { - let first = true; - /* eslint-disable max-depth */ - for (const next of turn) { - const nextPlayer = players.get(next); - if (!first) { - await msg.say(stripIndents` - Coward. - ${nextPlayer.user}, will YOU take the gun? - `); - } - if (first) first = false; - const keepGoing = await verify(msg.channel, nextPlayer.user); - if (keepGoing) break; - wallOfShame.push(players.get(next).user.toString()); - players.delete(next); - removeFromArray(turn, next); - } - /* eslint-enable max-depth */ - } - if (players.size === 1) winner = players.first(); - round++; - } - } - this.client.games.delete(msg.channel.id); - if (winner) { - return msg.say(stripIndents` - The winner is ${winner.user}! - - __**Wall Of Shame:**__ - ${wallOfShame.length ? wallOfShame.join('\n') : 'No one!'} + } + players.set(this.client.user.id, { + id: this.client.user.id, + user: this.client.user + }); + const turn = shuffle(players.map(player => player.id)); + const gun = shuffle([true, false, false, false, false, false, false, false]); + let round = 0; + let loser = null; + let winner = null; + const wallOfShame = []; + while (!loser && !winner) { + const player = players.get(turn[0]); + turn.push(turn[0]); + turn.shift(); + if (gun[round]) { + await msg.say(`**${player.user.tag}** pulls the trigger... **And dies!**`); + loser = player; + } else { + const nextUp = players.get(turn[0]).user; + await msg.say(stripIndents` + **${player.user.tag}** pulls the trigger... **And lives...** + ${nextUp.bot ? '' : `Will you take the gun, ${nextUp}?`} `); + if (!nextUp.bot) { + let first = true; + for (const next of turn) { + const nextPlayer = players.get(next); + if (!first) { + await msg.say(stripIndents` + Coward. + ${nextPlayer.user}, will YOU take the gun? + `); + } + if (first) first = false; + const keepGoing = await verify(msg.channel, nextPlayer.user); + if (keepGoing) break; + wallOfShame.push(players.get(next).user.toString()); + players.delete(next); + removeFromArray(turn, next); + } + } + if (players.size === 1) winner = players.first(); + round++; } + } + if (winner) { return msg.say(stripIndents` - The loser is ${loser.user}! + The winner is ${winner.user}! __**Wall Of Shame:**__ ${wallOfShame.length ? wallOfShame.join('\n') : 'No one!'} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; } + return msg.say(stripIndents` + The loser is ${loser.user}! + + __**Wall Of Shame:**__ + ${wallOfShame.length ? wallOfShame.join('\n') : 'No one!'} + `); } }; diff --git a/commands/games-mp/spam-war.js b/commands/games-mp/spam-war.js index 8f002b34..d5842ac2 100644 --- a/commands/games-mp/spam-war.js +++ b/commands/games-mp/spam-war.js @@ -10,6 +10,7 @@ module.exports = class SpamWarCommand extends Command { memberName: 'spam-war', description: 'See who can type more characters the fastest.', guildOnly: true, + game: true, args: [ { key: 'opponent', @@ -23,40 +24,28 @@ module.exports = class SpamWarCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - 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...'); - } - await msg.say('You get one point per character in your messages. You get 1 minute to spam.'); - await delay(5000); - await msg.say('You have **1 minute** to spam. Go!'); - const msgs = await msg.channel.awaitMessages({ - filter: res => [opponent.id, msg.author.id].includes(res.author.id), - time: 60000 - }); - const authorMsgs = msgs - .filter(authorMsg => authorMsg.author.id === msg.author.id) - .reduce((a, b) => a + b.content.length, 0); - const opponentMsgs = msgs - .filter(opponentMsg => opponentMsg.author.id === opponent.id) - .reduce((a, b) => a + b.content.length, 0); - const winner = authorMsgs > opponentMsgs ? msg.author : opponent; - const winnerPts = authorMsgs > opponentMsgs ? authorMsgs : opponentMsgs; - const loserPts = authorMsgs > opponentMsgs ? opponentMsgs : authorMsgs; - this.client.games.delete(msg.channel.id); - if (authorMsgs === opponentMsgs) { - return msg.say(`It was a tie! What are the odds? You both got **${winnerPts}** points!`); - } - return msg.say(`The winner is ${winner}, with **${winnerPts}** points! Your opponent only got ${loserPts}...`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + await msg.say('You get one point per character in your messages. You get 1 minute to spam.'); + await delay(5000); + await msg.say('You have **1 minute** to spam. Go!'); + const msgs = await msg.channel.awaitMessages({ + filter: res => [opponent.id, msg.author.id].includes(res.author.id), + time: 60000 + }); + const authorMsgs = msgs + .filter(authorMsg => authorMsg.author.id === msg.author.id) + .reduce((a, b) => a + b.content.length, 0); + const opponentMsgs = msgs + .filter(opponentMsg => opponentMsg.author.id === opponent.id) + .reduce((a, b) => a + b.content.length, 0); + const winner = authorMsgs > opponentMsgs ? msg.author : opponent; + const winnerPts = authorMsgs > opponentMsgs ? authorMsgs : opponentMsgs; + const loserPts = authorMsgs > opponentMsgs ? opponentMsgs : authorMsgs; + if (authorMsgs === opponentMsgs) { + return msg.say(`It was a tie! What are the odds? You both got **${winnerPts}** points!`); } + return msg.say(`The winner is ${winner}, with **${winnerPts}** points! Your opponent only got ${loserPts}...`); } }; diff --git a/commands/games-mp/tic-tac-toe.js b/commands/games-mp/tic-tac-toe.js index 572a6e39..2e4a4050 100644 --- a/commands/games-mp/tic-tac-toe.js +++ b/commands/games-mp/tic-tac-toe.js @@ -13,6 +13,7 @@ module.exports = class TicTacToeCommand extends Command { group: 'games-mp', memberName: 'tic-tac-toe', description: 'Play a game of tic-tac-toe with another user or the AI.', + game: true, args: [ { key: 'opponent', @@ -25,83 +26,71 @@ module.exports = class TicTacToeCommand extends Command { async run(msg, { opponent }) { if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 sides = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - const taken = []; - let userTurn = true; - let winner = null; - let lastTurnTimeout = false; - while (!winner && taken.length < 9) { - const user = userTurn ? msg.author : opponent; - const sign = userTurn ? 'X' : 'O'; - let choice; - if (opponent.bot && !userTurn) { - // eslint-disable-next-line new-cap - choice = ComputerMove(sides, { aiPlayer: 'O', huPlayer: 'X' }, 'Hard'); - } else { - await msg.say(stripIndents` - ${user}, which side do you pick? Type \`end\` to forfeit. - - ${this.displayBoard(sides)} - `); - const filter = res => { - if (res.author.id !== user.id) return false; - const pick = res.content; - if (pick.toLowerCase() === 'end') return true; - return valid.includes(pick) && !taken.includes(pick); - }; - const turn = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (!turn.size) { - await msg.say('Sorry, time is up!'); - if (lastTurnTimeout) { - winner = 'time'; - break; - } else { - userTurn = !userTurn; - lastTurnTimeout = true; - continue; - } - } - choice = turn.first().content; - if (choice.toLowerCase() === 'end') { - winner = userTurn ? opponent : msg.author; - break; - } - } - sides[opponent.bot && !userTurn ? choice : Number.parseInt(choice, 10) - 1] = sign; - taken.push(choice); - const win = this.verifyWin(sides, msg.author, opponent); - if (taken.length === 9 && !win) winner = 'tie'; - if (win) winner = win; - if (lastTurnTimeout) lastTurnTimeout = false; - userTurn = !userTurn; - } - this.client.games.delete(msg.channel.id); - if (winner === 'time') return msg.say('Game ended due to inactivity.'); - return msg.say(stripIndents` - ${winner === 'tie' ? 'Oh... The cat won.' : `Congrats, ${winner}!`} - - ${this.displayBoard(sides)} - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + if (!opponent.bot) { + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); } + const sides = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + const taken = []; + let userTurn = true; + let winner = null; + let lastTurnTimeout = false; + while (!winner && taken.length < 9) { + const user = userTurn ? msg.author : opponent; + const sign = userTurn ? 'X' : 'O'; + let choice; + if (opponent.bot && !userTurn) { + // eslint-disable-next-line new-cap + choice = ComputerMove(sides, { aiPlayer: 'O', huPlayer: 'X' }, 'Hard'); + } else { + await msg.say(stripIndents` + ${user}, which side do you pick? Type \`end\` to forfeit. + + ${this.displayBoard(sides)} + `); + const filter = res => { + if (res.author.id !== user.id) return false; + const pick = res.content; + if (pick.toLowerCase() === 'end') return true; + return valid.includes(pick) && !taken.includes(pick); + }; + const turn = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (!turn.size) { + await msg.say('Sorry, time is up!'); + if (lastTurnTimeout) { + winner = 'time'; + break; + } else { + userTurn = !userTurn; + lastTurnTimeout = true; + continue; + } + } + choice = turn.first().content; + if (choice.toLowerCase() === 'end') { + winner = userTurn ? opponent : msg.author; + break; + } + } + sides[opponent.bot && !userTurn ? choice : Number.parseInt(choice, 10) - 1] = sign; + taken.push(choice); + const win = this.verifyWin(sides, msg.author, opponent); + if (taken.length === 9 && !win) winner = 'tie'; + if (win) winner = win; + if (lastTurnTimeout) lastTurnTimeout = false; + userTurn = !userTurn; + } + if (winner === 'time') return msg.say('Game ended due to inactivity.'); + return msg.say(stripIndents` + ${winner === 'tie' ? 'Oh... The cat won.' : `Congrats, ${winner}!`} + + ${this.displayBoard(sides)} + `); } playerWon(board, player) { diff --git a/commands/games-mp/typing-race.js b/commands/games-mp/typing-race.js index f65be97b..7bd9822f 100644 --- a/commands/games-mp/typing-race.js +++ b/commands/games-mp/typing-race.js @@ -10,6 +10,7 @@ module.exports = class TypingRaceCommand extends Command { memberName: 'typing-race', description: 'Race a user to see who can type a sentence faster.', guildOnly: true, + game: true, args: [ { key: 'opponent', @@ -23,51 +24,39 @@ module.exports = class TypingRaceCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - 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 sentence = this.client.registry.commands.get('typing-test').generateSentence(5); - const img = await this.client.registry.commands.get('typing-test').generateImage(sentence); - await msg.say(`**Type the following sentence within 30 seconds:**`, { - files: [{ attachment: img, name: 'typing-race.png' }] - }); - const now = Date.now(); - const filter = res => { - if (![opponent.id, msg.author.id].includes(res.author.id)) return false; - return res.content.toLowerCase() === sentence; - }; - const winner = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - const newScore = Date.now() - now; - const highScoreGet = await this.client.redis.get('typing-test'); - const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; - const highScoreUser = await this.client.redis.get('typing-test-user'); - const scoreBeat = winner.size && (!highScore || highScore > newScore); - const user = await fetchHSUserDisplay(this.client, highScoreUser); - if (scoreBeat) { - await this.client.redis.set('typing-test', newScore); - await this.client.redis.set('typing-test-user', winner.first().author.id); - } - this.client.games.delete(msg.channel.id); - if (!winner.size) return msg.say('Oh... No one won.'); - const wpm = (sentence.length / 5) / ((newScore / 1000) / 60); - return msg.say(stripIndents` - The winner is ${winner.first().author}! (Took ${newScore / 1000} seconds, ${Math.round(wpm)} WPM) - ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore / 1000}s (Held by ${user}) - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + const sentence = this.client.registry.commands.get('typing-test').generateSentence(5); + const img = await this.client.registry.commands.get('typing-test').generateImage(sentence); + await msg.say(`**Type the following sentence within 30 seconds:**`, { + files: [{ attachment: img, name: 'typing-race.png' }] + }); + const now = Date.now(); + const filter = res => { + if (![opponent.id, msg.author.id].includes(res.author.id)) return false; + return res.content.toLowerCase() === sentence; + }; + const winner = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + const newScore = Date.now() - now; + const highScoreGet = await this.client.redis.get('typing-test'); + const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; + const highScoreUser = await this.client.redis.get('typing-test-user'); + const scoreBeat = winner.size && (!highScore || highScore > newScore); + const user = await fetchHSUserDisplay(this.client, highScoreUser); + if (scoreBeat) { + await this.client.redis.set('typing-test', newScore); + await this.client.redis.set('typing-test-user', winner.first().author.id); } + if (!winner.size) return msg.say('Oh... No one won.'); + const wpm = (sentence.length / 5) / ((newScore / 1000) / 60); + return msg.say(stripIndents` + The winner is ${winner.first().author}! (Took ${newScore / 1000} seconds, ${Math.round(wpm)} WPM) + ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore / 1000}s (Held by ${user}) + `); } }; diff --git a/commands/games-mp/word-chain.js b/commands/games-mp/word-chain.js index 1d3e5bae..f1c3f985 100644 --- a/commands/games-mp/word-chain.js +++ b/commands/games-mp/word-chain.js @@ -14,6 +14,7 @@ module.exports = class WordChainCommand extends Command { memberName: 'word-chain', description: 'Try to come up with words that start with the last letter of your opponent\'s word.', guildOnly: true, + game: true, credit: [ { name: 'Grady Ward', @@ -42,71 +43,59 @@ module.exports = class WordChainCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - 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...'); + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + const startWord = startWords[Math.floor(Math.random() * startWords.length)]; + await msg.say(stripIndents` + The start word will be **${startWord}**! You must answer within **${time}** seconds! + If you think your opponent has played a word that doesn't exist, respond with **challenge** on your turn. + Words cannot contain anything but letters. No numbers, spaces, or hyphens may be used. + The game will start in 5 seconds... + `); + await delay(5000); + let userTurn = Boolean(Math.floor(Math.random() * 2)); + const words = []; + let winner = null; + let lastWord = startWord; + while (!winner) { + const player = userTurn ? msg.author : opponent; + const letter = lastWord.charAt(lastWord.length - 1); + await msg.say(`It's ${player}'s turn! The letter is **${letter}**.`); + const filter = res => + res.author.id === player.id && /^[a-zA-Z']+$/i.test(res.content) && res.content.length < 50; + const wordChoice = await msg.channel.awaitMessages({ + filter, + max: 1, + time: time * 1000 + }); + if (!wordChoice.size) { + await msg.say('Time!'); + winner = userTurn ? opponent : msg.author; + break; } - const startWord = startWords[Math.floor(Math.random() * startWords.length)]; - await msg.say(stripIndents` - The start word will be **${startWord}**! You must answer within **${time}** seconds! - If you think your opponent has played a word that doesn't exist, respond with **challenge** on your turn. - Words cannot contain anything but letters. No numbers, spaces, or hyphens may be used. - The game will start in 5 seconds... - `); - await delay(5000); - let userTurn = Boolean(Math.floor(Math.random() * 2)); - const words = []; - let winner = null; - let lastWord = startWord; - while (!winner) { - const player = userTurn ? msg.author : opponent; - const letter = lastWord.charAt(lastWord.length - 1); - await msg.say(`It's ${player}'s turn! The letter is **${letter}**.`); - const filter = res => - res.author.id === player.id && /^[a-zA-Z']+$/i.test(res.content) && res.content.length < 50; - const wordChoice = await msg.channel.awaitMessages({ - filter, - max: 1, - time: time * 1000 - }); - if (!wordChoice.size) { - await msg.say('Time!'); - winner = userTurn ? opponent : msg.author; + const choice = wordChoice.first().content.toLowerCase(); + if (choice === 'challenge') { + const checked = await this.verifyWord(lastWord); + if (!checked) { + await msg.say(`Caught red-handed! **${lastWord}** is not valid!`); + winner = player; break; } - const choice = wordChoice.first().content.toLowerCase(); - if (choice === 'challenge') { - const checked = await this.verifyWord(lastWord); - if (!checked) { - await msg.say(`Caught red-handed! **${lastWord}** is not valid!`); - winner = player; - break; - } - await msg.say(`Sorry, **${lastWord}** is indeed valid!`); - continue; - } - if (!choice.startsWith(letter) || words.includes(choice)) { - await msg.say('Sorry! You lose!'); - winner = userTurn ? opponent : msg.author; - break; - } - words.push(choice); - lastWord = choice; - userTurn = !userTurn; + await msg.say(`Sorry, **${lastWord}** is indeed valid!`); + continue; } - this.client.games.delete(msg.channel.id); - if (!winner) return msg.say('Oh... No one won.'); - return msg.say(`The game is over! The winner is ${winner}!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + if (!choice.startsWith(letter) || words.includes(choice)) { + await msg.say('Sorry! You lose!'); + winner = userTurn ? opponent : msg.author; + break; + } + words.push(choice); + lastWord = choice; + userTurn = !userTurn; } + if (!winner) return msg.say('Oh... No one won.'); + return msg.say(`The game is over! The winner is ${winner}!`); } async verifyWord(word) { diff --git a/commands/games-mp/word-spud.js b/commands/games-mp/word-spud.js index a109c8ba..4638da5d 100644 --- a/commands/games-mp/word-spud.js +++ b/commands/games-mp/word-spud.js @@ -11,6 +11,7 @@ module.exports = class WordSpudCommand extends Command { memberName: 'word-spud', description: 'Hot potato, but with words.', guildOnly: true, + game: true, credit: [ { name: 'Jackbox Games', @@ -32,57 +33,45 @@ module.exports = class WordSpudCommand extends Command { if (opponent.bot) return msg.reply('Bots may not be played against.'); if (opponent.id === msg.author.id) return msg.reply('You may not play against yourself.'); if (this.client.blacklist.user.includes(opponent.id)) return msg.reply('This user is blacklisted.'); - 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 { - 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...'); - } - let currentWord = startWords[Math.floor(Math.random() * startWords.length)]; - let lastTurnTimeout = false; - let gameEnd = false; - let userTurn = false; - while (!gameEnd) { - const player = userTurn ? msg.author : opponent; - await msg.say(stripIndents` - ${player}, continue the chain: **${currentWord} ...?** - _Type \`end\` to end the game._ - `); - const filter = res => res.author.id === player.id && res.content.length <= 100; - const msgs = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (!msgs.size) { - await msg.say('No ideas? No problem, moving on.'); - userTurn = !userTurn; - if (lastTurnTimeout) { - break; - } else { - lastTurnTimeout = true; - continue; - } - } - const word = msgs.first().content.toLowerCase(); - if (word === 'end') { - gameEnd = true; - break; - } - await msg.say(`${currentWord} **${word}**? Cool!`); - currentWord = word; + await msg.say(`${opponent}, do you accept this challenge?`); + const verification = await verify(msg.channel, opponent); + if (!verification) return msg.say('Looks like they declined...'); + let currentWord = startWords[Math.floor(Math.random() * startWords.length)]; + let lastTurnTimeout = false; + let gameEnd = false; + let userTurn = false; + while (!gameEnd) { + const player = userTurn ? msg.author : opponent; + await msg.say(stripIndents` + ${player}, continue the chain: **${currentWord} ...?** + _Type \`end\` to end the game._ + `); + const filter = res => res.author.id === player.id && res.content.length <= 100; + const msgs = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (!msgs.size) { + await msg.say('No ideas? No problem, moving on.'); userTurn = !userTurn; - if (lastTurnTimeout) lastTurnTimeout = false; + if (lastTurnTimeout) { + break; + } else { + lastTurnTimeout = true; + continue; + } } - this.client.games.delete(msg.channel.id); - return msg.say('Thanks for playing!'); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const word = msgs.first().content.toLowerCase(); + if (word === 'end') { + gameEnd = true; + break; + } + await msg.say(`${currentWord} **${word}**? Cool!`); + currentWord = word; + userTurn = !userTurn; + if (lastTurnTimeout) lastTurnTimeout = false; } + return msg.say('Thanks for playing!'); } }; diff --git a/commands/games-sp/anagramica.js b/commands/games-sp/anagramica.js index ed6432bb..467168d4 100644 --- a/commands/games-sp/anagramica.js +++ b/commands/games-sp/anagramica.js @@ -15,6 +15,7 @@ module.exports = class AnagramicaCommand extends Command { group: 'games-sp', memberName: 'anagramica', description: 'Try to find all the anagrams for a given set of letters.', + game: true, credit: [ { name: 'Max Irwin', @@ -36,72 +37,63 @@ module.exports = class AnagramicaCommand extends Command { } async run(msg, { time }) { - 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.`); - try { - this.client.games.set(msg.channel.id, { name: this.name }); - const { valid, letters } = await this.fetchList(); - let points = 0; - await msg.reply(stripIndents` - **You have ${time} seconds to provide anagrams for the following letters:** - ${letters.map(letter => `\`${letter.toUpperCase()}\``).join(' ')} + const { valid, letters } = await this.fetchList(); + let points = 0; + await msg.reply(stripIndents` + **You have ${time} seconds to provide anagrams for the following letters:** + ${letters.map(letter => `\`${letter.toUpperCase()}\``).join(' ')} - _Need to see the list again? Type \`send list\` (or \`sl\`)._ - `); - const picked = []; - const filter = res => { - if (res.author.id !== msg.author.id) return false; - const choice = res.content.toLowerCase(); - if (choice === 'send list' || choice === 'sl') { - msg.reply(letters.map(letter => `\`${letter.toUpperCase()}\``).join(' ')).catch(() => null); - return true; - } - if (picked.includes(choice)) return false; - const score = this.getScore(letters, choice); - if (!score) return false; - if (!valid.includes(choice)) { - picked.push(choice); - reactIfAble(res, res.author, FAILURE_EMOJI_ID, '❌'); - return false; - } - points += score; - picked.push(choice); - reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); + _Need to see the list again? Type \`send list\` (or \`sl\`)._ + `); + const picked = []; + const filter = res => { + if (res.author.id !== msg.author.id) return false; + const choice = res.content.toLowerCase(); + if (choice === 'send list' || choice === 'sl') { + msg.reply(letters.map(letter => `\`${letter.toUpperCase()}\``).join(' ')).catch(() => null); return true; - }; - const msgs = await msg.channel.awaitMessages({ - filter, - time: time * 1000 - }); - const highScoreGet = await this.client.redis.get('anagramica'); - const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; - const highScoreUser = await this.client.redis.get('anagramica-user'); - const scoreBeat = !highScore || highScore < points; - const user = await fetchHSUserDisplay(this.client, highScoreUser); - if (scoreBeat) { - await this.client.redis.set('anagramica', points); - await this.client.redis.set('anagramica-user', msg.author.id); } - this.client.games.delete(msg.channel.id); - const moreWords = shuffle(valid.filter(word => !picked.includes(word))).slice(0, 5); - if (!msgs.size) { - return msg.reply(stripIndents` - Couldn't even think of one? Ouch. - ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore} (Held by ${user}) - - Here's some words you missed: ${moreWords.map(word => `\`${word}\``).join(', ')} - `); + if (picked.includes(choice)) return false; + const score = this.getScore(letters, choice); + if (!score) return false; + if (!valid.includes(choice)) { + picked.push(choice); + reactIfAble(res, res.author, FAILURE_EMOJI_ID, '❌'); + return false; } + points += score; + picked.push(choice); + reactIfAble(res, res.author, SUCCESS_EMOJI_ID, '✅'); + return true; + }; + const msgs = await msg.channel.awaitMessages({ + filter, + time: time * 1000 + }); + const highScoreGet = await this.client.redis.get('anagramica'); + const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; + const highScoreUser = await this.client.redis.get('anagramica-user'); + const scoreBeat = !highScore || highScore < points; + const user = await fetchHSUserDisplay(this.client, highScoreUser); + if (scoreBeat) { + await this.client.redis.set('anagramica', points); + await this.client.redis.set('anagramica-user', msg.author.id); + } + const moreWords = shuffle(valid.filter(word => !picked.includes(word))).slice(0, 5); + if (!msgs.size) { return msg.reply(stripIndents` - Nice job! Your final score was **${points}**! + Couldn't even think of one? Ouch. ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore} (Held by ${user}) Here's some words you missed: ${moreWords.map(word => `\`${word}\``).join(', ')} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } + return msg.reply(stripIndents` + Nice job! Your final score was **${points}**! + ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore} (Held by ${user}) + + Here's some words you missed: ${moreWords.map(word => `\`${word}\``).join(', ')} + `); } async fetchList() { diff --git a/commands/games-sp/anime-score.js b/commands/games-sp/anime-score.js index 9e78a53a..b5075d99 100644 --- a/commands/games-sp/anime-score.js +++ b/commands/games-sp/anime-score.js @@ -43,6 +43,7 @@ module.exports = class AnimeScoreCommand extends Command { group: 'games-sp', memberName: 'anime-score', description: 'See if you can guess what a random anime\'s score is.', + game: true, clientPermissions: ['EMBED_LINKS'], credit: [ { @@ -56,34 +57,30 @@ module.exports = class AnimeScoreCommand extends Command { } async run(msg) { - try { - const anime = await this.getRandomAnime(msg.channel.nsfw); - const embed = new MessageEmbed() - .setColor(0x02A9FF) - .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') - .setImage(anime.coverImage.large || anime.coverImage.medium || null) - .setTitle(anime.title.english || anime.title.romaji) - .setDescription(`_${anime.startDate.year}, ${formats[anime.format]}_`) - .setFooter(anime.id.toString()); - await msg.reply('**You have 15 seconds, what score do you think this anime has?**', { embeds: [embed] }); - const filter = res => { - if (res.author.id !== msg.author.id) return false; - return Boolean(Number.parseInt(res.content, 10)); - }; - const msgs = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 15000 - }); - if (!msgs.size) return msg.reply(`Sorry, time is up! It was **${anime.averageScore}%**.`); - const ans = Number.parseInt(msgs.first().content, 10); - const close = Math.abs(ans - anime.averageScore); - if (close <= 10 && close !== 0) return msg.reply(`Close! It was **${anime.averageScore}%**.`); - if (ans !== anime.averageScore) return msg.reply(`Nope, sorry, it was **${anime.averageScore}%**.`); - return msg.reply(`Nice job! It was **${anime.averageScore}%**!`); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const anime = await this.getRandomAnime(msg.channel.nsfw); + const embed = new MessageEmbed() + .setColor(0x02A9FF) + .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') + .setImage(anime.coverImage.large || anime.coverImage.medium || null) + .setTitle(anime.title.english || anime.title.romaji) + .setDescription(`_${anime.startDate.year}, ${formats[anime.format]}_`) + .setFooter(anime.id.toString()); + await msg.reply('**You have 15 seconds, what score do you think this anime has?**', { embeds: [embed] }); + const filter = res => { + if (res.author.id !== msg.author.id) return false; + return Boolean(Number.parseInt(res.content, 10)); + }; + const msgs = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 15000 + }); + if (!msgs.size) return msg.reply(`Sorry, time is up! It was **${anime.averageScore}%**.`); + const ans = Number.parseInt(msgs.first().content, 10); + const close = Math.abs(ans - anime.averageScore); + if (close <= 10 && close !== 0) return msg.reply(`Close! It was **${anime.averageScore}%**.`); + if (ans !== anime.averageScore) return msg.reply(`Nope, sorry, it was **${anime.averageScore}%**.`); + return msg.reply(`Nice job! It was **${anime.averageScore}%**!`); } async getRandomAnime(nsfw) { diff --git a/commands/games-sp/antidepressant-or-tolkien.js b/commands/games-sp/antidepressant-or-tolkien.js index e09411bd..282b5d55 100644 --- a/commands/games-sp/antidepressant-or-tolkien.js +++ b/commands/games-sp/antidepressant-or-tolkien.js @@ -26,6 +26,7 @@ module.exports = class AntidepressantOrTolkienCommand extends Command { group: 'games-sp', memberName: 'antidepressant-or-tokien', description: 'See if you can guess if a word is an Antidepressant or Tolkien character.', + game: true, credit: [ { name: 'Antidepressants or Tolkien', diff --git a/commands/games-sp/blackjack.js b/commands/games-sp/blackjack.js index ab3745a9..05b486e2 100644 --- a/commands/games-sp/blackjack.js +++ b/commands/games-sp/blackjack.js @@ -13,6 +13,7 @@ module.exports = class BlackjackCommand extends Command { group: 'games-sp', memberName: 'blackjack', description: 'Play a game of blackjack.', + game: true, args: [ { key: 'deckCount', @@ -27,93 +28,81 @@ module.exports = class BlackjackCommand extends Command { } async run(msg, { deckCount }) { - 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.`); - try { - this.client.games.set(msg.channel.id, { name: this.name, data: new Deck({ deckCount }) }); - const dealerHand = []; - this.draw(msg.channel, dealerHand); - this.draw(msg.channel, dealerHand); - const playerHand = []; - this.draw(msg.channel, playerHand); - this.draw(msg.channel, playerHand); - const dealerInitialTotal = this.calculate(dealerHand); - const playerInitialTotal = this.calculate(playerHand); - if (dealerInitialTotal === 21 && playerInitialTotal === 21) { - this.client.games.delete(msg.channel.id); - return msg.say('Well, both of you just hit blackjack. Right away. Rigged.'); - } else if (dealerInitialTotal === 21) { - this.client.games.delete(msg.channel.id); - return msg.say('Ouch, the dealer hit blackjack right away! Try again!'); - } else if (playerInitialTotal === 21) { - this.client.games.delete(msg.channel.id); - return msg.say('Wow, you hit blackjack right away! Lucky you!'); - } - let playerTurn = true; - let win = false; - let reason; - while (!win) { - if (playerTurn) { - await msg.say(stripIndents` - **First Dealer Card:** ${dealerHand[0].display} + const deck = new Deck({ deckCount }); + const dealerHand = []; + this.draw(dealerHand, deck); + this.draw(dealerHand, deck); + const playerHand = []; + this.draw(playerHand, deck); + this.draw(playerHand, deck); + const dealerInitialTotal = this.calculate(dealerHand); + const playerInitialTotal = this.calculate(playerHand); + if (dealerInitialTotal === 21 && playerInitialTotal === 21) { + return msg.say('Well, both of you just hit blackjack. Right away. Rigged.'); + } else if (dealerInitialTotal === 21) { + return msg.say('Ouch, the dealer hit blackjack right away! Try again!'); + } else if (playerInitialTotal === 21) { + return msg.say('Wow, you hit blackjack right away! Lucky you!'); + } + let playerTurn = true; + let win = false; + let reason; + while (!win) { + if (playerTurn) { + await msg.say(stripIndents` + **First Dealer Card:** ${dealerHand[0].display} - **You (${this.calculate(playerHand)}):** - ${playerHand.map(card => card.display).join('\n')} + **You (${this.calculate(playerHand)}):** + ${playerHand.map(card => card.display).join('\n')} - _Hit?_ - `); - const hit = await verify(msg.channel, msg.author, { extraYes: hitWords, extraNo: standWords }); - if (hit) { - const card = this.draw(msg.channel, playerHand); - const total = this.calculate(playerHand); - if (total > 21) { - reason = `You drew ${card.display}, total of ${total}! Bust`; - break; - } else if (total === 21) { - reason = `You drew ${card.display} and hit 21`; - win = true; - } - } else { - const dealerTotal = this.calculate(dealerHand); - await msg.say(`Second dealer card is ${dealerHand[1].display}, total of ${dealerTotal}.`); - playerTurn = false; + _Hit?_ + `); + const hit = await verify(msg.channel, msg.author, { extraYes: hitWords, extraNo: standWords }); + if (hit) { + const card = this.draw(playerHand, deck); + const total = this.calculate(playerHand); + if (total > 21) { + reason = `You drew ${card.display}, total of ${total}! Bust`; + break; + } else if (total === 21) { + reason = `You drew ${card.display} and hit 21`; + win = true; } } else { - const inital = this.calculate(dealerHand); - let card; - if (inital < 17) card = this.draw(msg.channel, dealerHand); - const total = this.calculate(dealerHand); - if (total > 21) { - reason = `Dealer drew ${card.display}, total of ${total}! Dealer bust`; - win = true; - } else if (total >= 17) { - const playerTotal = this.calculate(playerHand); - if (total === playerTotal) { - reason = `${card ? `Dealer drew ${card.display}, making it ` : ''}${playerTotal}-${total}`; - break; - } else if (total > playerTotal) { - reason = `${card ? `Dealer drew ${card.display}, making it ` : ''}${playerTotal}-**${total}**`; - break; - } else { - reason = `${card ? `Dealer drew ${card.display}, making it ` : ''}**${playerTotal}**-${total}`; - win = true; - } + const dealerTotal = this.calculate(dealerHand); + await msg.say(`Second dealer card is ${dealerHand[1].display}, total of ${dealerTotal}.`); + playerTurn = false; + } + } else { + const inital = this.calculate(dealerHand); + let card; + if (inital < 17) card = this.draw(dealerHand, deck); + const total = this.calculate(dealerHand); + if (total > 21) { + reason = `Dealer drew ${card.display}, total of ${total}! Dealer bust`; + win = true; + } else if (total >= 17) { + const playerTotal = this.calculate(playerHand); + if (total === playerTotal) { + reason = `${card ? `Dealer drew ${card.display}, making it ` : ''}${playerTotal}-${total}`; + break; + } else if (total > playerTotal) { + reason = `${card ? `Dealer drew ${card.display}, making it ` : ''}${playerTotal}-**${total}**`; + break; } else { - await msg.say(`Dealer drew ${card.display}, total of ${total}.`); + reason = `${card ? `Dealer drew ${card.display}, making it ` : ''}**${playerTotal}**-${total}`; + win = true; } + } else { + await msg.say(`Dealer drew ${card.display}, total of ${total}.`); } } - this.client.games.delete(msg.channel.id); - if (win) return msg.say(`${reason}! You won!`); - return msg.say(`${reason}! Too bad.`); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; } + if (win) return msg.say(`${reason}! You won!`); + return msg.say(`${reason}! Too bad.`); } - draw(channel, hand) { - const deck = this.client.games.get(channel.id).data; + draw(hand, deck) { const card = deck.draw(); hand.push(card); return card; diff --git a/commands/games-sp/box-choosing.js b/commands/games-sp/box-choosing.js index 22407f77..823b65bc 100644 --- a/commands/games-sp/box-choosing.js +++ b/commands/games-sp/box-choosing.js @@ -11,6 +11,7 @@ module.exports = class BoxChoosingCommand extends Command { group: 'games-sp', memberName: 'box-choosing', description: 'Do you believe that there are choices in life? Taken from Higurashi Chapter 4.', + game: true, credit: [ { name: '07th Expansion', @@ -31,61 +32,52 @@ module.exports = class BoxChoosingCommand extends Command { } async run(msg) { - 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 { - let i = 0; - let path = 'before'; - let end = false; - while (!end) { - const line = script[path][i]; - if (!line) { + let i = 0; + let path = 'before'; + let end = false; + while (!end) { + const line = script[path][i]; + if (!line) { + end = true; + break; + } + await msg.say(typeof line === 'object' ? line.text : stripIndents` + ${line} + + _Proceed?_ + `); + if (line.options) { + const filter = res => res.author.id === msg.author.id && line.options.includes(res.content.toLowerCase()); + const choose = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 120000 + }); + if (!choose.size) { end = true; break; } - await msg.say(typeof line === 'object' ? line.text : stripIndents` - ${line} - - _Proceed?_ - `); - if (line.options) { - const filter = res => res.author.id === msg.author.id && line.options.includes(res.content.toLowerCase()); - const choose = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 120000 - }); - if (!choose.size) { - end = true; - break; - } - path = ''; - const pick = line.paths[line.options.indexOf(choose.first().content.toLowerCase())]; - if ((this.red.has(msg.author.id) && pick !== 'red') || (this.blue.has(msg.author.id) && pick !== 'blue')) { - path += 'both'; - if (this.red.has(msg.author.id)) this.red.delete(msg.author.id); - if (this.blue.has(msg.author.id)) this.blue.delete(msg.author.id); - } else { - this[pick].add(msg.author.id); - setTimeout(() => { if (this[pick].has(msg.author.id)) this[pick].delete(msg.author.id); }, 600000); - } - path += pick; - i = 0; + path = ''; + const pick = line.paths[line.options.indexOf(choose.first().content.toLowerCase())]; + if ((this.red.has(msg.author.id) && pick !== 'red') || (this.blue.has(msg.author.id) && pick !== 'blue')) { + path += 'both'; + if (this.red.has(msg.author.id)) this.red.delete(msg.author.id); + if (this.blue.has(msg.author.id)) this.blue.delete(msg.author.id); } else { - const verification = await verify(msg.channel, msg.author, { time: 120000 }); - if (!verification) { - end = true; - break; - } - i++; + this[pick].add(msg.author.id); + setTimeout(() => { if (this[pick].has(msg.author.id)) this[pick].delete(msg.author.id); }, 600000); } + path += pick; + i = 0; + } else { + const verification = await verify(msg.channel, msg.author, { time: 120000 }); + if (!verification) { + end = true; + break; + } + i++; } - this.client.games.delete(msg.channel.id); - return msg.say(script.end); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; } + return msg.say(script.end); } }; diff --git a/commands/games-sp/captcha.js b/commands/games-sp/captcha.js index 7d882f24..dc9cdc86 100644 --- a/commands/games-sp/captcha.js +++ b/commands/games-sp/captcha.js @@ -15,6 +15,7 @@ module.exports = class CaptchaCommand extends Command { duration: 10 }, clientPermissions: ['ATTACH_FILES'], + game: true, credit: [ { name: 'Christoph Mueller', diff --git a/commands/games-sp/doors.js b/commands/games-sp/doors.js index 4bde659a..5e7108fe 100644 --- a/commands/games-sp/doors.js +++ b/commands/games-sp/doors.js @@ -11,6 +11,7 @@ module.exports = class DoorsCommand extends Command { group: 'games-sp', memberName: 'doors', description: 'Open the right door, and you win the money! Make the wrong choice, and you get the fire!', + game: true, credit: [ { name: 'Mythbusters', @@ -35,27 +36,18 @@ module.exports = class DoorsCommand extends Command { } async run(msg, { door }) { - 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 { - const win = doors[Math.floor(Math.random() * doors.length)]; - const noWin = doors.filter(thisDoor => thisDoor !== win && door !== thisDoor)[0]; - await msg.reply(stripIndents` - Well, there's nothing behind door number **${noWin}**. Do you want to stick with door ${door}? - ${this.emoji(1, noWin)} ${this.emoji(2, noWin)} ${this.emoji(3, noWin)} - `); - const stick = await verify(msg.channel, msg.author); - if (!stick) door = doors.filter(thisDoor => door !== thisDoor && thisDoor !== noWin)[0]; - this.client.games.delete(msg.channel.id); - return msg.reply(stripIndents` - ${door === win ? 'You chose wisely.' : 'Hmm... Try again.'} - ${this.emoji(1, noWin, win, door)} ${this.emoji(2, noWin, win, door)} ${this.emoji(3, noWin, win, door)} - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; - } + const win = doors[Math.floor(Math.random() * doors.length)]; + const noWin = doors.filter(thisDoor => thisDoor !== win && door !== thisDoor)[0]; + await msg.reply(stripIndents` + Well, there's nothing behind door number **${noWin}**. Do you want to stick with door ${door}? + ${this.emoji(1, noWin)} ${this.emoji(2, noWin)} ${this.emoji(3, noWin)} + `); + const stick = await verify(msg.channel, msg.author); + if (!stick) door = doors.filter(thisDoor => door !== thisDoor && thisDoor !== noWin)[0]; + return msg.reply(stripIndents` + ${door === win ? 'You chose wisely.' : 'Hmm... Try again.'} + ${this.emoji(1, noWin, win, door)} ${this.emoji(2, noWin, win, door)} ${this.emoji(3, noWin, win, door)} + `); } emoji(door, noWin, win, chosen) { diff --git a/commands/games-sp/google-feud.js b/commands/games-sp/google-feud.js index 9e1f9aec..019a1660 100644 --- a/commands/games-sp/google-feud.js +++ b/commands/games-sp/google-feud.js @@ -11,6 +11,7 @@ module.exports = class GoogleFeudCommand extends Command { group: 'games-sp', memberName: 'google-feud', description: 'Attempt to determine the top suggestions for a Google search.', + game: true, credit: [ { name: 'Google', @@ -27,46 +28,37 @@ module.exports = class GoogleFeudCommand extends Command { } async run(msg) { - 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 { - const question = questions[Math.floor(Math.random() * questions.length)]; - const suggestions = await this.fetchSuggestions(question); - if (!suggestions) return msg.say('Could not find any results.'); - const display = new Array(suggestions.length).fill('???'); - let tries = 4; - let score = 0; - while (display.includes('???') && tries) { - const embed = this.makeEmbed(question, tries, suggestions, display); - await msg.embed(embed); - const msgs = await msg.channel.awaitMessages({ - filter: res => res.author.id === msg.author.id, - max: 1, - time: 30000 - }); - if (!msgs.size) { - await msg.say('Time is up!'); - break; - } - const choice = msgs.first().content.toLowerCase(); - if (suggestions.includes(choice)) { - score += 10000 - (suggestions.indexOf(choice) * 1000); - display[suggestions.indexOf(choice)] = choice; - } else { - --tries; - } + const question = questions[Math.floor(Math.random() * questions.length)]; + const suggestions = await this.fetchSuggestions(question); + if (!suggestions) return msg.say('Could not find any results.'); + const display = new Array(suggestions.length).fill('???'); + let tries = 4; + let score = 0; + while (display.includes('???') && tries) { + const embed = this.makeEmbed(question, tries, suggestions, display); + await msg.embed(embed); + const msgs = await msg.channel.awaitMessages({ + filter: res => res.author.id === msg.author.id, + max: 1, + time: 30000 + }); + if (!msgs.size) { + await msg.say('Time is up!'); + break; } - this.client.games.delete(msg.channel.id); - if (!display.includes('???')) { - return msg.say(`You win! Nice job, master of Google!\n**Final Score: $${formatNumber(score)}**`); + const choice = msgs.first().content.toLowerCase(); + if (suggestions.includes(choice)) { + score += 10000 - (suggestions.indexOf(choice) * 1000); + display[suggestions.indexOf(choice)] = choice; + } else { + --tries; } - const final = this.makeEmbed(question, tries, suggestions, suggestions); - return msg.say(`Better luck next time!\n**Final Score: $${formatNumber(score)}**`, { embeds: [final] }); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } + if (!display.includes('???')) { + return msg.say(`You win! Nice job, master of Google!\n**Final Score: $${formatNumber(score)}**`); + } + const final = this.makeEmbed(question, tries, suggestions, suggestions); + return msg.say(`Better luck next time!\n**Final Score: $${formatNumber(score)}**`, { embeds: [final] }); } async fetchSuggestions(question) { diff --git a/commands/games-sp/guess-song.js b/commands/games-sp/guess-song.js index 4d2f2309..13fd695e 100644 --- a/commands/games-sp/guess-song.js +++ b/commands/games-sp/guess-song.js @@ -22,6 +22,7 @@ module.exports = class GuessSongCommand extends Command { guildOnly: true, userPermissions: ['CONNECT', 'SPEAK'], clientPermissions: ['ATTACH_FILES'], + game: true, credit: [ { name: 'Spotify', @@ -51,35 +52,25 @@ module.exports = class GuessSongCommand extends Command { const usage = this.client.registry.commands.get('join').usage(); return msg.reply(`I am not in a voice channel. Use ${usage} to fix that!`); } - 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.`); if (!connection.canPlay) return msg.reply('I am already playing audio in this server.'); - this.client.games.set(msg.channel.id, { name: this.name }); - let songID; - try { - if (!this.token) await this.fetchToken(); - const data = await this.fetchRandomSong(chart); - const { body: previewBody } = await request.get(data.preview); - connection.play(Readable.from([previewBody])); - await reactIfAble(msg, this.client.user, '🔉'); - await msg.reply('**You have 30 seconds, what song is this?**'); - const msgs = await msg.channel.awaitMessages({ - filter: res => res.author.id === msg.author.id, - max: 1, - time: 30000 - }); - this.client.games.delete(msg.channel.id); - connection.stop(); - if (!msgs.size) return msg.reply(`Time! It's **${data.name}** by **${data.artist}**!`); - const guess = msgs.first().content.toLowerCase(); - if (!guess.includes(data.name.toLowerCase()) && !guess.includes(data.shortName.toLowerCase())) { - return msg.reply(`Nope! It's **${data.name}** by **${data.artist}**!`); - } - return msg.reply(`Nice! It's **${data.name}** by **${data.artist}**!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Song ID: \`${songID}\`.`); + if (!this.token) await this.fetchToken(); + const data = await this.fetchRandomSong(chart); + const { body: previewBody } = await request.get(data.preview); + connection.play(Readable.from([previewBody])); + await reactIfAble(msg, this.client.user, '🔉'); + await msg.reply('**You have 30 seconds, what song is this?**'); + const msgs = await msg.channel.awaitMessages({ + filter: res => res.author.id === msg.author.id, + max: 1, + time: 30000 + }); + connection.stop(); + if (!msgs.size) return msg.reply(`Time! It's **${data.name}** by **${data.artist}**!`); + const guess = msgs.first().content.toLowerCase(); + if (!guess.includes(data.name.toLowerCase()) && !guess.includes(data.shortName.toLowerCase())) { + return msg.reply(`Nope! It's **${data.name}** by **${data.artist}**!`); } + return msg.reply(`Nice! It's **${data.name}** by **${data.artist}**!`); } async fetchCharts(playlist) { diff --git a/commands/games-sp/hangman.js b/commands/games-sp/hangman.js index 9a1f1e22..5fd7e339 100644 --- a/commands/games-sp/hangman.js +++ b/commands/games-sp/hangman.js @@ -11,6 +11,7 @@ module.exports = class HangmanCommand extends Command { group: 'games-sp', memberName: 'hangman', description: 'Prevent a man from being hanged by guessing a word as fast as you can.', + game: true, credit: [ { name: 'Grady Ward', @@ -29,82 +30,73 @@ module.exports = class HangmanCommand extends Command { } async run(msg) { - 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 { - const word = words[Math.floor(Math.random() * words.length)].toLowerCase(); - let points = 0; - let displayText = null; - let guessed = false; - const confirmation = []; - const incorrect = []; - const display = new Array(word.length).fill('_'); - while (word.length !== confirmation.length && points < 6) { - await msg.say(stripIndents` - ${displayText === null ? 'Here we go!' : displayText ? 'Good job!' : 'Nope!'} - \`${display.join(' ')}\`. Which letter do you choose? Type \`end\` to forfeit. - Incorrect Tries: ${incorrect.join(', ') || 'None'} - \`\`\` - ___________ - | | - | ${points > 0 ? 'O' : ''} - | ${points > 2 ? '—' : ' '}${points > 1 ? '|' : ''}${points > 3 ? '—' : ''} - | ${points > 4 ? '/' : ''} ${points > 5 ? '\\' : ''} - =========== - \`\`\` - `); - const filter = res => { - const choice = res.content.toLowerCase(); - return res.author.id === msg.author.id && !confirmation.includes(choice) && !incorrect.includes(choice); - }; - const guess = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - if (!guess.size) { - await msg.say('Sorry, time is up!'); - break; - } - const choice = guess.first().content.toLowerCase(); - if (choice === 'end') break; - if (choice.length > 1 && choice === word) { - guessed = true; - break; - } else if (word.includes(choice)) { - displayText = true; - for (let i = 0; i < word.length; i++) { - if (word.charAt(i) !== choice) continue; // eslint-disable-line max-depth - confirmation.push(word.charAt(i)); - display[i] = word.charAt(i); - } - } else { - displayText = false; - if (choice.length === 1) incorrect.push(choice); - points++; - } + const word = words[Math.floor(Math.random() * words.length)].toLowerCase(); + let points = 0; + let displayText = null; + let guessed = false; + const confirmation = []; + const incorrect = []; + const display = new Array(word.length).fill('_'); + while (word.length !== confirmation.length && points < 6) { + await msg.say(stripIndents` + ${displayText === null ? 'Here we go!' : displayText ? 'Good job!' : 'Nope!'} + \`${display.join(' ')}\`. Which letter do you choose? Type \`end\` to forfeit. + Incorrect Tries: ${incorrect.join(', ') || 'None'} + \`\`\` + ___________ + | | + | ${points > 0 ? 'O' : ''} + | ${points > 2 ? '—' : ' '}${points > 1 ? '|' : ''}${points > 3 ? '—' : ''} + | ${points > 4 ? '/' : ''} ${points > 5 ? '\\' : ''} + =========== + \`\`\` + `); + const filter = res => { + const choice = res.content.toLowerCase(); + return res.author.id === msg.author.id && !confirmation.includes(choice) && !incorrect.includes(choice); + }; + const guess = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + if (!guess.size) { + await msg.say('Sorry, time is up!'); + break; } - this.client.games.delete(msg.channel.id); - const defined = await this.defineWord(word); - if (word.length === confirmation.length || guessed) { - return msg.say(stripIndents` - You won, it was ${word}! - - ${defined ? `**${defined.name}** (${defined.partOfSpeech})` : ''} - ${defined ? defined.definiton : ''} - `); + const choice = guess.first().content.toLowerCase(); + if (choice === 'end') break; + if (choice.length > 1 && choice === word) { + guessed = true; + break; + } else if (word.includes(choice)) { + displayText = true; + for (let i = 0; i < word.length; i++) { + if (word.charAt(i) !== choice) continue; // eslint-disable-line max-depth + confirmation.push(word.charAt(i)); + display[i] = word.charAt(i); + } + } else { + displayText = false; + if (choice.length === 1) incorrect.push(choice); + points++; } + } + const defined = await this.defineWord(word); + if (word.length === confirmation.length || guessed) { return msg.say(stripIndents` - Too bad... It was ${word}... + You won, it was ${word}! ${defined ? `**${defined.name}** (${defined.partOfSpeech})` : ''} ${defined ? defined.definiton : ''} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } + return msg.say(stripIndents` + Too bad... It was ${word}... + + ${defined ? `**${defined.name}** (${defined.partOfSpeech})` : ''} + ${defined ? defined.definiton : ''} + `); } async defineWord(word) { diff --git a/commands/games-sp/hearing-test.js b/commands/games-sp/hearing-test.js index 2ab4adda..31f81315 100644 --- a/commands/games-sp/hearing-test.js +++ b/commands/games-sp/hearing-test.js @@ -18,6 +18,7 @@ module.exports = class HearingTestCommand extends Command { }, guildOnly: true, userPermissions: ['CONNECT', 'SPEAK'], + game: true, credit: [ { name: 'Noise addicts', @@ -36,37 +37,33 @@ module.exports = class HearingTestCommand extends Command { return msg.reply(`I am not in a voice channel. Use ${usage} to fix that!`); } if (!connection.canPlay) return msg.reply('I am already playing audio in this server.'); - try { - let age; - let range; - let previousAge = 'all'; - let previousRange = 8; - for (const { age: dataAge, khz, file } of data) { - connection.play(path.join(__dirname, '..', '..', 'assets', 'sounds', 'hearing-test', file)); - await delay(3500); - await msg.reply('Did you hear that sound? Reply with **[y]es** or **[n]o**.'); - const heard = await verify(msg.channel, msg.author); - if (!heard || file === data[data.length - 1].file) { - age = previousAge; - range = previousRange; - break; - } - previousAge = dataAge; - previousRange = khz; - } - if (age === 'all') return msg.reply('Everyone should be able to hear that. You cannot hear.'); - if (age === 'max') { - return msg.reply(stripIndents` - You can hear any frequency of which a human is capable. - The maximum frequency you were able to hear was **${range}000hz**. - `); + let age; + let range; + let previousAge = 'all'; + let previousRange = 8; + for (const { age: dataAge, khz, file } of data) { + connection.play(path.join(__dirname, '..', '..', 'assets', 'sounds', 'hearing-test', file)); + await delay(3500); + await msg.reply('Did you hear that sound? Reply with **[y]es** or **[n]o**.'); + const heard = await verify(msg.channel, msg.author); + if (!heard || file === data[data.length - 1].file) { + age = previousAge; + range = previousRange; + break; } + previousAge = dataAge; + previousRange = khz; + } + if (age === 'all') return msg.reply('Everyone should be able to hear that. You cannot hear.'); + if (age === 'max') { return msg.reply(stripIndents` - You have the hearing of someone **${Number.parseInt(age, 10) + 1} or older**. + You can hear any frequency of which a human is capable. The maximum frequency you were able to hear was **${range}000hz**. `); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } + return msg.reply(stripIndents` + You have the hearing of someone **${Number.parseInt(age, 10) + 1} or older**. + The maximum frequency you were able to hear was **${range}000hz**. + `); } }; diff --git a/commands/games-sp/horse-race.js b/commands/games-sp/horse-race.js index f21edc3c..cdd79032 100644 --- a/commands/games-sp/horse-race.js +++ b/commands/games-sp/horse-race.js @@ -20,6 +20,7 @@ module.exports = class HorseRaceCommand extends Command { duration: 10 }, clientPermissions: ['ATTACH_FILES'], + game: true, credit: [ { name: 'Ambition', diff --git a/commands/games-sp/hunger-games.js b/commands/games-sp/hunger-games.js index 5d42ba4f..3c89f553 100644 --- a/commands/games-sp/hunger-games.js +++ b/commands/games-sp/hunger-games.js @@ -11,6 +11,7 @@ module.exports = class HungerGamesCommand extends Command { group: 'games-sp', memberName: 'hunger-games', description: 'Simulate a Hunger Games match with up to 24 tributes.', + game: true, credit: [ { name: 'BrantSteele', @@ -36,55 +37,43 @@ module.exports = class HungerGamesCommand extends Command { if (removeDuplicates(tributes).length !== tributes.length) { return msg.reply('Please do not enter the same tribute twice.'); } - 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 { - let sun = true; - let turn = 0; - let bloodbath = true; - const kills = {}; - for (const tribute of tributes) kills[tribute] = 0; - const remaining = new Set(shuffle(tributes)); - while (remaining.size > 1) { - if (!bloodbath && sun) ++turn; - const sunEvents = bloodbath ? events.bloodbath : sun ? events.day : events.night; - const results = []; - const deaths = []; - this.makeEvents(remaining, kills, sunEvents, deaths, results); - let text = stripIndents` - __**${bloodbath ? 'Bloodbath' : sun ? `Day ${turn}` : `Night ${turn}`}:**__ - ${results.join('\n')} + let sun = true; + let turn = 0; + let bloodbath = true; + const kills = {}; + for (const tribute of tributes) kills[tribute] = 0; + const remaining = new Set(shuffle(tributes)); + while (remaining.size > 1) { + if (!bloodbath && sun) ++turn; + const sunEvents = bloodbath ? events.bloodbath : sun ? events.day : events.night; + const results = []; + const deaths = []; + this.makeEvents(remaining, kills, sunEvents, deaths, results); + let text = stripIndents` + __**${bloodbath ? 'Bloodbath' : sun ? `Day ${turn}` : `Night ${turn}`}:**__ + ${results.join('\n')} + `; + if (deaths.length) { + text += '\n\n'; + text += stripIndents` + **${deaths.length} cannon shot${deaths.length === 1 ? '' : 's'} can be heard in the distance.** + ${deaths.join('\n')} `; - if (deaths.length) { - text += '\n\n'; - text += stripIndents` - **${deaths.length} cannon shot${deaths.length === 1 ? '' : 's'} can be heard in the distance.** - ${deaths.join('\n')} - `; - } - text += `\n\n_Proceed?_`; - await msg.say(text); - const verification = await verify(msg.channel, msg.author, { time: 120000 }); - if (!verification) { - this.client.games.delete(msg.channel.id); - return msg.say('See you next time!'); - } - if (!bloodbath) sun = !sun; - if (bloodbath) bloodbath = false; } - this.client.games.delete(msg.channel.id); - const remainingArr = Array.from(remaining); - return msg.say(stripIndents` - And the winner is... **${remainingArr[0]}**! - - __**Kills Leaderboard:**__ - ${this.makeLeaderboard(tributes, kills).join('\n')} - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + text += `\n\n_Proceed?_`; + await msg.say(text); + const verification = await verify(msg.channel, msg.author, { time: 120000 }); + if (!verification) return msg.say('See you next time!'); + if (!bloodbath) sun = !sun; + if (bloodbath) bloodbath = false; } + const remainingArr = Array.from(remaining); + return msg.say(stripIndents` + And the winner is... **${remainingArr[0]}**! + + __**Kills Leaderboard:**__ + ${this.makeLeaderboard(tributes, kills).join('\n')} + `); } parseEvent(event, tributes) { diff --git a/commands/games-sp/jeopardy.js b/commands/games-sp/jeopardy.js index 382807d3..94ac900c 100644 --- a/commands/games-sp/jeopardy.js +++ b/commands/games-sp/jeopardy.js @@ -16,6 +16,7 @@ module.exports = class JeopardyCommand extends Command { usages: 2, duration: 10 }, + game: true, credit: [ { name: 'jService', @@ -38,39 +39,30 @@ module.exports = class JeopardyCommand extends Command { } async run(msg) { - 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.`); - try { - this.client.games.set(msg.channel.id, { name: this.name }); - const question = await this.fetchQuestion(); - const clueCard = await this.generateClueCard(question.question.replace(/<\/?i>/gi, '')); - const connection = msg.guild ? this.client.dispatchers.get(msg.guild.id) : null; - let playing = false; - if (msg.guild && connection && connection.canPlay) { - playing = true; - connection.play(path.join(__dirname, '..', '..', 'assets', 'sounds', 'jeopardy.mp3')); - await reactIfAble(msg, this.client.user, '🔉'); - } - const category = question.category ? question.category.title.toUpperCase() : ''; - await msg.reply(`${category ? `The category is: **${category}**. ` : ''}30 seconds, good luck.`, { - files: [{ attachment: clueCard, name: 'clue-card.png' }] - }); - const msgs = await msg.channel.awaitMessages({ - filter: res => res.author.id === msg.author.id, - max: 1, - time: 30000 - }); - if (playing) connection.stop(); - const answer = question.answer.replace(/<\/?i>/gi, '*').replace(/\(|\)/g, ''); - this.client.games.delete(msg.channel.id); - if (!msgs.size) return msg.reply(`Time's up, the answer was **${answer}**.`); - const win = msgs.first().content.toLowerCase() === answer.toLowerCase(); - if (!win) return msg.reply(`The answer was **${answer}**.`); - return msg.reply(`The answer was **${answer}**. Good job!`); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const question = await this.fetchQuestion(); + const clueCard = await this.generateClueCard(question.question.replace(/<\/?i>/gi, '')); + const connection = msg.guild ? this.client.dispatchers.get(msg.guild.id) : null; + let playing = false; + if (msg.guild && connection && connection.canPlay) { + playing = true; + connection.play(path.join(__dirname, '..', '..', 'assets', 'sounds', 'jeopardy.mp3')); + await reactIfAble(msg, this.client.user, '🔉'); } + const category = question.category ? question.category.title.toUpperCase() : ''; + await msg.reply(`${category ? `The category is: **${category}**. ` : ''}30 seconds, good luck.`, { + files: [{ attachment: clueCard, name: 'clue-card.png' }] + }); + const msgs = await msg.channel.awaitMessages({ + filter: res => res.author.id === msg.author.id, + max: 1, + time: 30000 + }); + if (playing) connection.stop(); + const answer = question.answer.replace(/<\/?i>/gi, '*').replace(/\(|\)/g, ''); + if (!msgs.size) return msg.reply(`Time's up, the answer was **${answer}**.`); + const win = msgs.first().content.toLowerCase() === answer.toLowerCase(); + if (!win) return msg.reply(`The answer was **${answer}**.`); + return msg.reply(`The answer was **${answer}**. Good job!`); } async fetchQuestion() { diff --git a/commands/games-sp/mad-libs.js b/commands/games-sp/mad-libs.js index 34173e08..78dde267 100644 --- a/commands/games-sp/mad-libs.js +++ b/commands/games-sp/mad-libs.js @@ -9,6 +9,7 @@ module.exports = class MadLibsCommand extends Command { group: 'games-sp', memberName: 'mad-libs', description: 'Choose words that fill in the blanks to create a crazy story!', + game: true, credit: [ { name: 'Mad Libs', @@ -25,39 +26,30 @@ module.exports = class MadLibsCommand extends Command { } async run(msg) { - 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 { - const lib = libs[Math.floor(Math.random() * libs.length)]; - const choices = []; - for (const word of lib.needed) { - await msg.reply(`Give me a **${word}**.`); - const filter = res => { - if (res.author.id !== msg.author.id) return false; - if (!res.content || res.content.length > 12) { - msg.reply('Please only use a maximum of 12 characters per word.').catch(() => null); - return false; - } - return true; - }; - const choice = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 120000 - }); - if (!choice.size) break; - choices.push(choice.first().content); - } - this.client.games.delete(msg.channel.id); - let finished = lib.text; - for (let i = 0; i < choices.length; i++) { - finished = finished.replaceAll(`{${i}}`, `**${choices[i]}**`); - } - return msg.say(finished); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const lib = libs[Math.floor(Math.random() * libs.length)]; + const choices = []; + for (const word of lib.needed) { + await msg.reply(`Give me a **${word}**.`); + const filter = res => { + if (res.author.id !== msg.author.id) return false; + if (!res.content || res.content.length > 12) { + msg.reply('Please only use a maximum of 12 characters per word.').catch(() => null); + return false; + } + return true; + }; + const choice = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 120000 + }); + if (!choice.size) break; + choices.push(choice.first().content); } + let finished = lib.text; + for (let i = 0; i < choices.length; i++) { + finished = finished.replaceAll(`{${i}}`, `**${choices[i]}**`); + } + return msg.say(finished); } }; diff --git a/commands/games-sp/math-quiz.js b/commands/games-sp/math-quiz.js index cc6f44de..c26e5113 100644 --- a/commands/games-sp/math-quiz.js +++ b/commands/games-sp/math-quiz.js @@ -27,6 +27,7 @@ module.exports = class MathQuizCommand extends Command { memberName: 'math-quiz', description: 'See how fast you can answer a math problem in a given time limit.', details: `**Difficulties:** ${difficulties.join(', ')}`, + game: true, args: [ { key: 'difficulty', diff --git a/commands/games-sp/memory.js b/commands/games-sp/memory.js index 155f92ed..35a00e62 100644 --- a/commands/games-sp/memory.js +++ b/commands/games-sp/memory.js @@ -12,6 +12,7 @@ module.exports = class MemoryCommand extends Command { group: 'games-sp', memberName: 'memory', description: 'Test your memory.', + game: true, args: [ { key: 'level', @@ -24,33 +25,24 @@ module.exports = class MemoryCommand extends Command { } async run(msg, { level }) { - 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 { - const memorize = this.genArray(level); - const memorizeDisplay = memorize.map(word => `\`${word.toUpperCase()}\``).join(' '); - const memorizeMsg = await msg.say(stripIndents` - **You have 10 seconds to memorize:** - ${memorizeDisplay} - `); - await delay(10000); - await memorizeMsg.edit('Type what you saw. Don\'t worry about formatting, just the words.'); - const memorizeType = memorize.join(' '); - const msgs = await msg.channel.awaitMessages({ - filter: res => msg.author.id === res.author.id, - max: 1, - time: 30000 - }); - this.client.games.delete(msg.channel.id); - if (!msgs.size) return msg.say(`Sorry, time is up! It was ${memorizeDisplay}.`); - const answer = msgs.first().content.toLowerCase(); - if (answer !== memorizeType) return msg.say(`Sorry, you typed it wrong. It was ${memorizeDisplay}.`); - return msg.say('Nice job! 10/10! You deserve some cake!'); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; - } + const memorize = this.genArray(level); + const memorizeDisplay = memorize.map(word => `\`${word.toUpperCase()}\``).join(' '); + const memorizeMsg = await msg.say(stripIndents` + **You have 10 seconds to memorize:** + ${memorizeDisplay} + `); + await delay(10000); + await memorizeMsg.edit('Type what you saw. Don\'t worry about formatting, just the words.'); + const memorizeType = memorize.join(' '); + const msgs = await msg.channel.awaitMessages({ + filter: res => msg.author.id === res.author.id, + max: 1, + time: 30000 + }); + if (!msgs.size) return msg.say(`Sorry, time is up! It was ${memorizeDisplay}.`); + const answer = msgs.first().content.toLowerCase(); + if (answer !== memorizeType) return msg.say(`Sorry, you typed it wrong. It was ${memorizeDisplay}.`); + return msg.say('Nice job! 10/10! You deserve some cake!'); } genArray(level) { diff --git a/commands/games-sp/minesweeper.js b/commands/games-sp/minesweeper.js index 1d099e69..db662e52 100644 --- a/commands/games-sp/minesweeper.js +++ b/commands/games-sp/minesweeper.js @@ -15,6 +15,7 @@ module.exports = class MinesweeperCommand extends Command { group: 'games-sp', memberName: 'minesweeper', description: 'Play a game of Minesweeper.', + game: true, args: [ { key: 'size', @@ -28,116 +29,107 @@ module.exports = class MinesweeperCommand extends Command { } async run(msg, { size }) { - 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.`); - try { - const game = new BombSweeper(size, size); - this.client.games.set(msg.channel.id, { name: this.name, data: game }); - game.PlaceBombs(size + 1); // eslint-disable-line new-cap - let win = null; - game.onWin = () => { win = true; }; - game.onLoss = () => { win = false; }; - const flagged = []; - const startTime = new Date(); - let cheatMode = false; - while (win === null) { - const currentTime = moment.duration(new Date() - startTime).format('mm:ss'); - await msg.say(stripIndents` - ${msg.author}, what coordinates do you pick (ex. 4,5)? Type \`end\` to forfeit. - Type \`flag \` to flag a spot as a bomb. To remove a flag, run it again. - You can also use ranges to mark multiple spots (ex. 4-7,5 or 7,4-5). + const game = new BombSweeper(size, size); + game.PlaceBombs(size + 1); // eslint-disable-line new-cap + let win = null; + game.onWin = () => { win = true; }; + game.onLoss = () => { win = false; }; + const flagged = []; + const startTime = new Date(); + let cheatMode = false; + while (win === null) { + const currentTime = moment.duration(new Date() - startTime).format('mm:ss'); + await msg.say(stripIndents` + ${msg.author}, what coordinates do you pick (ex. 4,5)? Type \`end\` to forfeit. + Type \`flag \` to flag a spot as a bomb. To remove a flag, run it again. + You can also use ranges to mark multiple spots (ex. 4-7,5 or 7,4-5). - ${this.displayBoard(game.board, game.mask, flagged, cheatMode)} - **Total Mines:** ${size + 1} | **Flagged:** ${flagged.length} | **Time:** ${currentTime} - `); - const filter = res => { - if (res.author.id !== msg.author.id) return false; - const pick = res.content; - if (pick.toLowerCase() === 'xyzzy' && !cheatMode) return true; - if (pick.toLowerCase() === 'end') return true; - const coordPicked = pick.match(turnRegex); - if (!coordPicked) return false; - const x = Number.parseInt(coordPicked[2], 10); - const y = Number.parseInt(coordPicked[4], 10); - if (x > size || y > size || x < 1 || y < 1) return false; - if (game.mask[y - 1][x - 1]) return false; - return true; - }; - const turn = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 120000 - }); - if (!turn.size) { - await msg.say('Sorry, time is up!'); - break; - } - const choice = turn.first().content; - if (choice.toLowerCase() === 'end') { - win = false; - break; - } - if (choice.toLowerCase() === 'xyzzy') { - cheatMode = true; - await msg.say('Cheat mode is now active. No high score will be saved.'); - continue; - } - const coordPicked = choice.match(turnRegex); + ${this.displayBoard(game.board, game.mask, flagged, cheatMode)} + **Total Mines:** ${size + 1} | **Flagged:** ${flagged.length} | **Time:** ${currentTime} + `); + const filter = res => { + if (res.author.id !== msg.author.id) return false; + const pick = res.content; + if (pick.toLowerCase() === 'xyzzy' && !cheatMode) return true; + if (pick.toLowerCase() === 'end') return true; + const coordPicked = pick.match(turnRegex); + if (!coordPicked) return false; const x = Number.parseInt(coordPicked[2], 10); const y = Number.parseInt(coordPicked[4], 10); - const xRange = coordPicked[3] ? Math.abs(Number.parseInt(coordPicked[3], 10)) : null; - const yRange = coordPicked[5] ? Math.abs(Number.parseInt(coordPicked[5], 10)) : null; - const flag = Boolean(coordPicked[1]); - if (xRange && yRange) { - await msg.say('You cannot have both an X and Y range.'); - continue; - } - if ((yRange && flag) || (xRange && flag)) { - await msg.say('You cannot flag a range.'); - continue; - } - if (xRange) { - for (let i = x; i <= xRange; i++) { - const keepGoing = await this.runResult(msg, game, i, y, flag, flagged, win); - if (keepGoing === false) break; - if (keepGoing === null) continue; - } - } else if (yRange) { - for (let i = y; i <= yRange; i++) { - const keepGoing = await this.runResult(msg, game, x, i, flag, flagged, win); - if (keepGoing === false) break; - if (keepGoing === null) continue; - } - } else { - const keepGoing = await this.runResult(msg, game, x, y, flag, flagged, win); + if (x > size || y > size || x < 1 || y < 1) return false; + if (game.mask[y - 1][x - 1]) return false; + return true; + }; + const turn = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 120000 + }); + if (!turn.size) { + await msg.say('Sorry, time is up!'); + break; + } + const choice = turn.first().content; + if (choice.toLowerCase() === 'end') { + win = false; + break; + } + if (choice.toLowerCase() === 'xyzzy') { + cheatMode = true; + await msg.say('Cheat mode is now active. No high score will be saved.'); + continue; + } + const coordPicked = choice.match(turnRegex); + const x = Number.parseInt(coordPicked[2], 10); + const y = Number.parseInt(coordPicked[4], 10); + const xRange = coordPicked[3] ? Math.abs(Number.parseInt(coordPicked[3], 10)) : null; + const yRange = coordPicked[5] ? Math.abs(Number.parseInt(coordPicked[5], 10)) : null; + const flag = Boolean(coordPicked[1]); + if (xRange && yRange) { + await msg.say('You cannot have both an X and Y range.'); + continue; + } + if ((yRange && flag) || (xRange && flag)) { + await msg.say('You cannot flag a range.'); + continue; + } + if (xRange) { + for (let i = x; i <= xRange; i++) { + const keepGoing = await this.runResult(msg, game, i, y, flag, flagged, win); if (keepGoing === false) break; if (keepGoing === null) continue; } + } else if (yRange) { + for (let i = y; i <= yRange; i++) { + const keepGoing = await this.runResult(msg, game, x, i, flag, flagged, win); + if (keepGoing === false) break; + if (keepGoing === null) continue; + } + } else { + const keepGoing = await this.runResult(msg, game, x, y, flag, flagged, win); + if (keepGoing === false) break; + if (keepGoing === null) continue; } - const newScore = Date.now() - startTime; - const highScoreGet = await this.client.redis.get(`minesweeper-${size}`); - const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; - const highScoreUser = await this.client.redis.get(`minesweeper-${size}-user`); - const scoreBeat = !cheatMode && win && (!highScore || highScore > newScore); - const user = await fetchHSUserDisplay(this.client, highScoreUser); - if (scoreBeat) { - await this.client.redis.set(`minesweeper-${size}`, newScore); - await this.client.redis.set(`minesweeper-${size}-user`, msg.author.id); - } - this.client.games.delete(msg.channel.id); - if (win === null) return msg.say('Game ended due to inactivity.'); - const newDisplayTime = moment.duration(newScore).format('mm:ss'); - const displayTime = moment.duration(highScore).format('mm:ss'); - return msg.say(stripIndents` - ${win ? `Nice job! You win! (Took ${newDisplayTime})` : 'Sorry... You lose.'} - ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${displayTime} (Held by ${user}) - - ${this.displayBoard(game.board)} - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; } + const newScore = Date.now() - startTime; + const highScoreGet = await this.client.redis.get(`minesweeper-${size}`); + const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; + const highScoreUser = await this.client.redis.get(`minesweeper-${size}-user`); + const scoreBeat = !cheatMode && win && (!highScore || highScore > newScore); + const user = await fetchHSUserDisplay(this.client, highScoreUser); + if (scoreBeat) { + await this.client.redis.set(`minesweeper-${size}`, newScore); + await this.client.redis.set(`minesweeper-${size}-user`, msg.author.id); + } + if (win === null) return msg.say('Game ended due to inactivity.'); + const newDisplayTime = moment.duration(newScore).format('mm:ss'); + const displayTime = moment.duration(highScore).format('mm:ss'); + return msg.say(stripIndents` + ${win ? `Nice job! You win! (Took ${newDisplayTime})` : 'Sorry... You lose.'} + ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${displayTime} (Held by ${user}) + + ${this.displayBoard(game.board)} + `); } async runResult(msg, game, x, y, flag, flagged, win) { diff --git a/commands/games-sp/pokemon-advantage.js b/commands/games-sp/pokemon-advantage.js index 8ea2549b..3e6da0e3 100644 --- a/commands/games-sp/pokemon-advantage.js +++ b/commands/games-sp/pokemon-advantage.js @@ -19,6 +19,7 @@ module.exports = class PokemonAdvantageCommand extends Command { duration: 10 }, clientPermissions: ['ATTACH_FILES'], + game: true, credit: [ { name: 'Pokémon', @@ -64,44 +65,35 @@ module.exports = class PokemonAdvantageCommand extends Command { } async run(msg) { - 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 { - const num1 = Math.floor(Math.random() * (this.client.pokemon.pokemonCount + 1)); - const pkmn1 = await this.client.pokemon.fetch(num1); - const num2 = Math.floor(Math.random() * (this.client.pokemon.pokemonCount + 1)); - const pkmn2 = await this.client.pokemon.fetch(num2); - await pkmn1.fetchDefaultVariety(); - await pkmn2.fetchDefaultVariety(); - const attachment = await this.createImage(pkmn1, pkmn2, null); - const answer = this.calculateAdvantage(pkmn1, pkmn2); - const answerAttachment = await this.createImage(pkmn1, pkmn2, answer); - await msg.reply(stripIndents` - **You have 15 seconds, who\'s got the type advantage?** - _If the Pokémon are evenly matched, type \`even\`._ - `, { files: [attachment] }); - const msgs = await msg.channel.awaitMessages({ - filter: res => res.author.id === msg.author.id, - max: 1, - time: 15000 - }); - this.client.games.delete(msg.channel.id); - if (!msgs.size) return msg.reply(`Time! It's **${answer.name}**!`, { files: [answerAttachment] }); - const guess = msgs.first().content.toLowerCase(); - const slug = this.client.pokemon.makeSlug(guess); - if (answer === true) { - if (guess === 'even') return msg.reply('Nice! These two are even!', { files: [answerAttachment] }); - return msg.reply('Nope! These two are even!', { files: [answerAttachment] }); - } - if (!answer.names.includes(guess) && answer.slug !== slug) { - return msg.reply(`Nope! It's **${answer.name}**!`, { files: [answerAttachment] }); - } - return msg.reply(`Nice! It's **${answer.name}**!`, { files: [answerAttachment] }); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const num1 = Math.floor(Math.random() * (this.client.pokemon.pokemonCount + 1)); + const pkmn1 = await this.client.pokemon.fetch(num1); + const num2 = Math.floor(Math.random() * (this.client.pokemon.pokemonCount + 1)); + const pkmn2 = await this.client.pokemon.fetch(num2); + await pkmn1.fetchDefaultVariety(); + await pkmn2.fetchDefaultVariety(); + const attachment = await this.createImage(pkmn1, pkmn2, null); + const answer = this.calculateAdvantage(pkmn1, pkmn2); + const answerAttachment = await this.createImage(pkmn1, pkmn2, answer); + await msg.reply(stripIndents` + **You have 15 seconds, who\'s got the type advantage?** + _If the Pokémon are evenly matched, type \`even\`._ + `, { files: [attachment] }); + const msgs = await msg.channel.awaitMessages({ + filter: res => res.author.id === msg.author.id, + max: 1, + time: 15000 + }); + if (!msgs.size) return msg.reply(`Time! It's **${answer.name}**!`, { files: [answerAttachment] }); + const guess = msgs.first().content.toLowerCase(); + const slug = this.client.pokemon.makeSlug(guess); + if (answer === true) { + if (guess === 'even') return msg.reply('Nice! These two are even!', { files: [answerAttachment] }); + return msg.reply('Nope! These two are even!', { files: [answerAttachment] }); } + if (!answer.names.includes(guess) && answer.slug !== slug) { + return msg.reply(`Nope! It's **${answer.name}**!`, { files: [answerAttachment] }); + } + return msg.reply(`Nice! It's **${answer.name}**!`, { files: [answerAttachment] }); } async createImage(pokemon1, pokemon2, winner) { diff --git a/commands/games-sp/quiz.js b/commands/games-sp/quiz.js index 4075d169..8a367f5a 100644 --- a/commands/games-sp/quiz.js +++ b/commands/games-sp/quiz.js @@ -14,6 +14,7 @@ module.exports = class QuizCommand extends Command { memberName: 'quiz', description: 'Answer a quiz question.', details: `**Difficulties:** ${difficulties.join(', ')}`, + game: true, credit: [ { name: 'Open Trivia DB', @@ -35,37 +36,33 @@ module.exports = class QuizCommand extends Command { } async run(msg, { difficulty }) { - try { - const { body } = await request - .get('https://opentdb.com/api.php') - .query({ - amount: 1, - type: 'multiple', - encode: 'url3986', - difficulty - }); - if (!body.results) return msg.reply('Oh no, a question could not be fetched. Try again later!'); - const answers = body.results[0].incorrect_answers.map(answer => decodeURIComponent(answer.toLowerCase())); - const correct = decodeURIComponent(body.results[0].correct_answer.toLowerCase()); - answers.push(correct); - const shuffled = shuffle(answers); - await msg.reply(stripIndents` - **You have 15 seconds. The category is _${decodeURIComponent(body.results[0].category)}_.** - ${decodeURIComponent(body.results[0].question)} - ${shuffled.map((answer, i) => `**${choices[i]}.** ${answer}`).join('\n')} - `); - const filter = res => res.author.id === msg.author.id && choices.includes(res.content.toUpperCase()); - const msgs = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 15000 + const { body } = await request + .get('https://opentdb.com/api.php') + .query({ + amount: 1, + type: 'multiple', + encode: 'url3986', + difficulty }); - if (!msgs.size) return msg.reply(`Sorry, time is up! It was ${correct}.`); - const win = shuffled[choices.indexOf(msgs.first().content.toUpperCase())] === correct; - if (!win) return msg.reply(`Nope, sorry, it's ${correct}.`); - return msg.reply('Nice job! 10/10! You deserve some cake!'); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + if (!body.results) return msg.reply('Oh no, a question could not be fetched. Try again later!'); + const answers = body.results[0].incorrect_answers.map(answer => decodeURIComponent(answer.toLowerCase())); + const correct = decodeURIComponent(body.results[0].correct_answer.toLowerCase()); + answers.push(correct); + const shuffled = shuffle(answers); + await msg.reply(stripIndents` + **You have 15 seconds. The category is _${decodeURIComponent(body.results[0].category)}_.** + ${decodeURIComponent(body.results[0].question)} + ${shuffled.map((answer, i) => `**${choices[i]}.** ${answer}`).join('\n')} + `); + const filter = res => res.author.id === msg.author.id && choices.includes(res.content.toUpperCase()); + const msgs = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 15000 + }); + if (!msgs.size) return msg.reply(`Sorry, time is up! It was ${correct}.`); + const win = shuffled[choices.indexOf(msgs.first().content.toUpperCase())] === correct; + if (!win) return msg.reply(`Nope, sorry, it's ${correct}.`); + return msg.reply('Nice job! 10/10! You deserve some cake!'); } }; diff --git a/commands/games-sp/reaction-time.js b/commands/games-sp/reaction-time.js index 583f2cb6..1d1d4fa1 100644 --- a/commands/games-sp/reaction-time.js +++ b/commands/games-sp/reaction-time.js @@ -10,45 +10,37 @@ module.exports = class ReactionTimeCommand extends Command { aliases: ['reaction', 'react', 'gunfight-sp', 'sp-gunfight'], group: 'games-sp', memberName: 'reaction-time', - description: 'Test your reaction time.' + description: 'Test your reaction time.', + game: true }); } async run(msg) { - 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 { - await msg.say('Get Ready...'); - await delay(randomRange(1000, 30000)); - const word = words[Math.floor(Math.random() * words.length)]; - await msg.say(`TYPE \`${word.toUpperCase()}\` NOW!`); - const filter = res => msg.author.id === res.author.id && res.content.toLowerCase() === word; - const now = Date.now(); - const msgs = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 30000 - }); - const newScore = Date.now() - now; - const highScoreGet = await this.client.redis.get('reaction-time'); - const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; - const highScoreUser = await this.client.redis.get('reaction-time-user'); - const scoreBeat = !highScore || highScore > newScore; - const user = await fetchHSUserDisplay(this.client, highScoreUser); - if (scoreBeat) { - await this.client.redis.set('reaction-time', newScore); - await this.client.redis.set('reaction-time-user', msg.author.id); - } - this.client.games.delete(msg.channel.id); - if (!msgs.size) return msg.say('Failed to answer within 30 seconds.'); - return msg.say(stripIndents` - Nice one! (Took ${newScore / 1000} seconds) - ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore / 1000} (Held by ${user}) - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + await msg.say('Get Ready...'); + await delay(randomRange(1000, 30000)); + const word = words[Math.floor(Math.random() * words.length)]; + await msg.say(`TYPE \`${word.toUpperCase()}\` NOW!`); + const filter = res => msg.author.id === res.author.id && res.content.toLowerCase() === word; + const now = Date.now(); + const msgs = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 30000 + }); + const newScore = Date.now() - now; + const highScoreGet = await this.client.redis.get('reaction-time'); + const highScore = highScoreGet ? Number.parseInt(highScoreGet, 10) : null; + const highScoreUser = await this.client.redis.get('reaction-time-user'); + const scoreBeat = !highScore || highScore > newScore; + const user = await fetchHSUserDisplay(this.client, highScoreUser); + if (scoreBeat) { + await this.client.redis.set('reaction-time', newScore); + await this.client.redis.set('reaction-time-user', msg.author.id); } + if (!msgs.size) return msg.say('Failed to answer within 30 seconds.'); + return msg.say(stripIndents` + Nice one! (Took ${newScore / 1000} seconds) + ${scoreBeat ? `**New High Score!** Old:` : `High Score:`} ${highScore / 1000} (Held by ${user}) + `); } }; diff --git a/commands/games-sp/sorting-hat.js b/commands/games-sp/sorting-hat.js index d5926b54..3922c3ef 100644 --- a/commands/games-sp/sorting-hat.js +++ b/commands/games-sp/sorting-hat.js @@ -12,6 +12,7 @@ module.exports = class SortingHatCommand extends Command { group: 'games-sp', memberName: 'sorting-hat', description: 'Take a quiz to determine your Hogwarts house.', + game: true, credit: [ { name: 'Pottermore', @@ -29,64 +30,52 @@ module.exports = class SortingHatCommand extends Command { } async run(msg) { - 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 { - const points = { - g: 0, - s: 0, - h: 0, - r: 0 - }; - const blacklist = []; - const questionNums = ['2', '3', '4', '5', '6', '7']; - let turn = 1; - while (turn < 9) { - let question; - if (turn === 1) { - question = questions.first[Math.floor(Math.random() * questions.first.length)]; - } else if (turn === 8) { - question = questions.last[Math.floor(Math.random() * questions.last.length)]; - } else { - const possible = questionNums.filter(num => !blacklist.includes(num)); - const value = possible[Math.floor(Math.random() * possible.length)]; - const group = questions[value]; - blacklist.push(value); - question = group[Math.floor(Math.random() * group.length)]; - } - const answers = shuffle(question.answers); - await msg.say(stripIndents` - **${turn}.** ${question.text} - ${answers.map((answer, i) => `- **${choices[i]}.** ${answer.text}`).join('\n')} - `); - const filter = res => - res.author.id === msg.author.id && choices.slice(0, answers.length).includes(res.content.toUpperCase()); - const choice = await msg.channel.awaitMessages({ - filter, - max: 1, - time: 120000 - }); - if (!choice.size) { - this.client.games.delete(msg.channel.id); - return msg.say('Oh no, you ran out of time! Too bad.'); - } - const answer = answers[choices.indexOf(choice.first().content.toUpperCase())]; - for (const [house, amount] of Object.entries(answer.points)) points[house] += amount; - ++turn; + const points = { + g: 0, + s: 0, + h: 0, + r: 0 + }; + const blacklist = []; + const questionNums = ['2', '3', '4', '5', '6', '7']; + let turn = 1; + while (turn < 9) { + let question; + if (turn === 1) { + question = questions.first[Math.floor(Math.random() * questions.first.length)]; + } else if (turn === 8) { + question = questions.last[Math.floor(Math.random() * questions.last.length)]; + } else { + const possible = questionNums.filter(num => !blacklist.includes(num)); + const value = possible[Math.floor(Math.random() * possible.length)]; + const group = questions[value]; + blacklist.push(value); + question = group[Math.floor(Math.random() * group.length)]; } - const houseResult = Object.keys(points).filter(h => points[h] > 0).sort((a, b) => points[b] - points[a]); - this.client.games.delete(msg.channel.id); - const totalPoints = houseResult.reduce((a, b) => a + points[b], 0); - return msg.say(stripIndents` - You are a member of... **${houses[houseResult[0]]}**! - _${descriptions[houseResult[0]]}_ - - ${houseResult.map(house => `${houses[house]}: ${Math.round((points[house] / totalPoints) * 100)}%`).join('\n')} + const answers = shuffle(question.answers); + await msg.say(stripIndents` + **${turn}.** ${question.text} + ${answers.map((answer, i) => `- **${choices[i]}.** ${answer.text}`).join('\n')} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + const filter = res => + res.author.id === msg.author.id && choices.slice(0, answers.length).includes(res.content.toUpperCase()); + const choice = await msg.channel.awaitMessages({ + filter, + max: 1, + time: 120000 + }); + if (!choice.size) return msg.say('Oh no, you ran out of time! Too bad.'); + const answer = answers[choices.indexOf(choice.first().content.toUpperCase())]; + for (const [house, amount] of Object.entries(answer.points)) points[house] += amount; + ++turn; } + const houseResult = Object.keys(points).filter(h => points[h] > 0).sort((a, b) => points[b] - points[a]); + const totalPoints = houseResult.reduce((a, b) => a + points[b], 0); + return msg.say(stripIndents` + You are a member of... **${houses[houseResult[0]]}**! + _${descriptions[houseResult[0]]}_ + + ${houseResult.map(house => `${houses[house]}: ${Math.round((points[house] / totalPoints) * 100)}%`).join('\n')} + `); } }; diff --git a/commands/games-sp/tarot.js b/commands/games-sp/tarot.js index ba66c929..aee2ca4e 100644 --- a/commands/games-sp/tarot.js +++ b/commands/games-sp/tarot.js @@ -13,6 +13,7 @@ module.exports = class TarotCommand extends Command { group: 'games-sp', memberName: 'tarot', description: 'Provides a fortune using Tarot cards.', + game: true, credit: [ { name: 'dariusk', @@ -50,38 +51,29 @@ module.exports = class TarotCommand extends Command { } async run(msg, { question }) { - 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 { - const deck = new TarotDeck(); - const cards = deck.draw(3); - for (let i = 0; i < cards.length; i++) { - const card = cards[i]; - await msg.say(stripIndents` - Your ${displayNums[i]} card is **${card.name}**. - This card is often associated with words like **${list(card.keywords)}**. + const deck = new TarotDeck(); + const cards = deck.draw(3); + for (let i = 0; i < cards.length; i++) { + const card = cards[i]; + await msg.say(stripIndents` + Your ${displayNums[i]} card is **${card.name}**. + This card is often associated with words like **${list(card.keywords)}**. - One common meaning for this card is **${card.randomLightMeaning()}**. - However, beware, as it could also mean **${card.randomShadowMeaning()}**. + One common meaning for this card is **${card.randomLightMeaning()}**. + However, beware, as it could also mean **${card.randomShadowMeaning()}**. - Would you like me to keep going? Type **[y]es** or **[n]o**. - `, { files: [card.imagePath] }); - const verification = await verify(msg.channel, msg.author); - if (!verification) break; - } - this.client.games.delete(msg.channel.id); - return msg.say(stripIndents` - To finish with a recap, you asked the question: **${escapeMarkdown(question)}** - - In response, the following cards were drawn: - - ${cards.map(card => `${card.name} (${card.keywords.join(', ')})`).join('\n- ')} - - I hope this gives you a good idea of what the future holds... - `); - } catch (err) { - this.client.games.delete(msg.channel.id); - throw err; + Would you like me to keep going? Type **[y]es** or **[n]o**. + `, { files: [card.imagePath] }); + const verification = await verify(msg.channel, msg.author); + if (!verification) break; } + return msg.say(stripIndents` + To finish with a recap, you asked the question: **${escapeMarkdown(question)}** + + In response, the following cards were drawn: + - ${cards.map(card => `${card.name} (${card.keywords.join(', ')})`).join('\n- ')} + + I hope this gives you a good idea of what the future holds... + `); } }; diff --git a/commands/games-sp/typing-test.js b/commands/games-sp/typing-test.js index 1948f84f..491d70b8 100644 --- a/commands/games-sp/typing-test.js +++ b/commands/games-sp/typing-test.js @@ -11,7 +11,8 @@ module.exports = class TypingTestCommand extends Command { name: 'typing-test', group: 'games-sp', memberName: 'typing-test', - description: 'See how fast you can type a sentence.' + description: 'See how fast you can type a sentence.', + game: true }); } diff --git a/commands/games-sp/whos-that-pokemon-cry.js b/commands/games-sp/whos-that-pokemon-cry.js index f1d9ba29..24ccf2f2 100644 --- a/commands/games-sp/whos-that-pokemon-cry.js +++ b/commands/games-sp/whos-that-pokemon-cry.js @@ -16,6 +16,7 @@ module.exports = class WhosThatPokemonCryCommand extends Command { guildOnly: true, userPermissions: ['CONNECT', 'SPEAK'], clientPermissions: ['ATTACH_FILES'], + game: true, credit: [ { name: 'Pokémon', @@ -82,34 +83,25 @@ module.exports = class WhosThatPokemonCryCommand extends Command { const usage = this.client.registry.commands.get('join').usage(); return msg.reply(`I am not in a voice channel. Use ${usage} to fix that!`); } - 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.`); if (!connection.canPlay) return msg.reply('I am already playing audio in this server.'); - this.client.games.set(msg.channel.id, { name: this.name }); - try { - const data = await this.client.pokemon.fetch(pokemon.toString()); - const names = data.names.map(name => name.name.toLowerCase()); - const attachment = await this.client.registry.commands.get('whos-that-pokemon').createImage(data, false); - connection.play(data.cry); - await reactIfAble(msg, this.client.user, '🔉'); - await msg.reply('**You have 15 seconds, who\'s that Pokémon?**'); - const msgs = await msg.channel.awaitMessages({ - filter: res => res.author.id === msg.author.id, - max: 1, - time: 15000 - }); - connection.play(data.cry); - this.client.games.delete(msg.channel.id); - if (!msgs.size) return msg.reply(`Time! It's **${data.name}**!`, { files: [attachment] }); - const guess = msgs.first().content.toLowerCase(); - const slug = this.client.pokemon.makeSlug(guess); - if (!names.includes(guess) && data.slug !== slug) { - return msg.reply(`Nope! It's **${data.name}**!`, { files: [attachment] }); - } - return msg.reply(`Nice! It's **${data.name}**!`, { files: [attachment] }); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const data = await this.client.pokemon.fetch(pokemon.toString()); + const names = data.names.map(name => name.name.toLowerCase()); + const attachment = await this.client.registry.commands.get('whos-that-pokemon').createImage(data, false); + connection.play(data.cry); + await reactIfAble(msg, this.client.user, '🔉'); + await msg.reply('**You have 15 seconds, who\'s that Pokémon?**'); + const msgs = await msg.channel.awaitMessages({ + filter: res => res.author.id === msg.author.id, + max: 1, + time: 15000 + }); + connection.play(data.cry); + if (!msgs.size) return msg.reply(`Time! It's **${data.name}**!`, { files: [attachment] }); + const guess = msgs.first().content.toLowerCase(); + const slug = this.client.pokemon.makeSlug(guess); + if (!names.includes(guess) && data.slug !== slug) { + return msg.reply(`Nope! It's **${data.name}**!`, { files: [attachment] }); } + return msg.reply(`Nice! It's **${data.name}**!`, { files: [attachment] }); } }; diff --git a/commands/games-sp/whos-that-pokemon.js b/commands/games-sp/whos-that-pokemon.js index 88e4e1c6..cd581931 100644 --- a/commands/games-sp/whos-that-pokemon.js +++ b/commands/games-sp/whos-that-pokemon.js @@ -18,6 +18,7 @@ module.exports = class WhosThatPokemonCommand extends Command { duration: 10 }, clientPermissions: ['ATTACH_FILES'], + game: true, credit: [ { name: 'Pokémon', @@ -84,38 +85,29 @@ module.exports = class WhosThatPokemonCommand extends Command { } async run(msg, { pokemon }) { - 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 { - const data = await this.client.pokemon.fetch(pokemon.toString()); - const names = data.names.map(name => name.name.toLowerCase()); - const attachment = await this.createImage(data, true); - const answerAttachment = await this.createImage(data, false); - const connection = msg.guild ? this.client.dispatchers.get(msg.guild.id) : null; - if (msg.guild && connection && connection.canPlay) { - connection.play(path.join(__dirname, '..', '..', 'assets', 'sounds', 'whos-that-pokemon.mp3')); - await reactIfAble(msg, this.client.user, '🔉'); - } - await msg.reply('**You have 15 seconds, who\'s that Pokémon?**', { files: [attachment] }); - const msgs = await msg.channel.awaitMessages({ - filter: res => res.author.id === msg.author.id, - max: 1, - time: 15000 - }); - if (connection && data.cry) connection.play(data.cry); - this.client.games.delete(msg.channel.id); - if (!msgs.size) return msg.reply(`Time! It's **${data.name}**!`, { files: [answerAttachment] }); - const guess = msgs.first().content.toLowerCase(); - const slug = this.client.pokemon.makeSlug(guess); - if (!names.includes(guess) && data.slug !== slug) { - return msg.reply(`Nope! It's **${data.name}**!`, { files: [answerAttachment] }); - } - return msg.reply(`Nice! It's **${data.name}**!`, { files: [answerAttachment] }); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const data = await this.client.pokemon.fetch(pokemon.toString()); + const names = data.names.map(name => name.name.toLowerCase()); + const attachment = await this.createImage(data, true); + const answerAttachment = await this.createImage(data, false); + const connection = msg.guild ? this.client.dispatchers.get(msg.guild.id) : null; + if (msg.guild && connection && connection.canPlay) { + connection.play(path.join(__dirname, '..', '..', 'assets', 'sounds', 'whos-that-pokemon.mp3')); + await reactIfAble(msg, this.client.user, '🔉'); } + await msg.reply('**You have 15 seconds, who\'s that Pokémon?**', { files: [attachment] }); + const msgs = await msg.channel.awaitMessages({ + filter: res => res.author.id === msg.author.id, + max: 1, + time: 15000 + }); + if (connection && data.cry) connection.play(data.cry); + if (!msgs.size) return msg.reply(`Time! It's **${data.name}**!`, { files: [answerAttachment] }); + const guess = msgs.first().content.toLowerCase(); + const slug = this.client.pokemon.makeSlug(guess); + if (!names.includes(guess) && data.slug !== slug) { + return msg.reply(`Nope! It's **${data.name}**!`, { files: [answerAttachment] }); + } + return msg.reply(`Nice! It's **${data.name}**!`, { files: [answerAttachment] }); } async createImage(pokemon, hide) { diff --git a/commands/games-sp/would-you-rather.js b/commands/games-sp/would-you-rather.js index 4f9e6907..70b2bc35 100644 --- a/commands/games-sp/would-you-rather.js +++ b/commands/games-sp/would-you-rather.js @@ -12,6 +12,7 @@ module.exports = class WouldYouRatherCommand extends Command { group: 'games-sp', memberName: 'would-you-rather', description: 'Responds with a random "Would you rather ...?" question.', + game: true, credit: [ { name: 'wouldurather.io', @@ -25,44 +26,34 @@ module.exports = class WouldYouRatherCommand extends Command { } async run(msg) { - 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 (!this.availableQuestions) await this.fetchAvailableQuestions(); - const data = await this.fetchRandomQuestion(); - await msg.say(stripIndents` - Would you rather... - **1.** ${data.option1} - **2.** ${data.option2} + if (!this.availableQuestions) await this.fetchAvailableQuestions(); + const data = await this.fetchRandomQuestion(); + await msg.say(stripIndents` + Would you rather... + **1.** ${data.option1} + **2.** ${data.option2} - _Respond with either **1** or **2** to continue._ - `); - const filter = res => res.author.id === msg.author.id && choices.includes(res.content.toLowerCase()); - const msgs = await msg.channel.awaitMessages({ - filter, - time: 30000, - max: 1 - }); - if (!msgs.size) { - this.client.games.delete(msg.channel.id); - return msg.reply(stripIndents` - No response? Too bad. - ${formatNumber(data.option1Votes)} - ${formatNumber(data.option2Votes)} - `); - } - const option1 = msgs.first().content.toLowerCase() === '1'; - const totalVotes = Number.parseInt(data.option1Votes, 10) + Number.parseInt(data.option2Votes, 10); - const numToUse = option1 ? Number.parseInt(data.option1Votes, 10) : Number.parseInt(data.option2Votes, 10); - this.client.games.delete(msg.channel.id); + _Respond with either **1** or **2** to continue._ + `); + const filter = res => res.author.id === msg.author.id && choices.includes(res.content.toLowerCase()); + const msgs = await msg.channel.awaitMessages({ + filter, + time: 30000, + max: 1 + }); + if (!msgs.size) { return msg.reply(stripIndents` - **${Math.round((numToUse / totalVotes) * 100)}%** of people agree! + No response? Too bad. ${formatNumber(data.option1Votes)} - ${formatNumber(data.option2Votes)} `); - } catch (err) { - this.client.games.delete(msg.channel.id); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } + const option1 = msgs.first().content.toLowerCase() === '1'; + const totalVotes = Number.parseInt(data.option1Votes, 10) + Number.parseInt(data.option2Votes, 10); + const numToUse = option1 ? Number.parseInt(data.option1Votes, 10) : Number.parseInt(data.option2Votes, 10); + return msg.reply(stripIndents` + **${Math.round((numToUse / totalVotes) * 100)}%** of people agree! + ${formatNumber(data.option1Votes)} - ${formatNumber(data.option2Votes)} + `); } async fetchAvailableQuestions() { diff --git a/commands/pokedex/pokedex-ability.js b/commands/pokedex/pokedex-ability.js index 7d947d94..a62f8182 100644 --- a/commands/pokedex/pokedex-ability.js +++ b/commands/pokedex/pokedex-ability.js @@ -32,16 +32,12 @@ module.exports = class PokedexAbilityCommand extends Command { } async run(msg, { ability }) { - try { - const data = await this.client.pokemon.abilities.fetch(ability); - if (!data) return msg.say('Could not find any results.'); - const embed = new MessageEmbed() - .setColor(0xED1C24) - .setTitle(data.name) - .setDescription(data.description || 'No description available.'); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const data = await this.client.pokemon.abilities.fetch(ability); + if (!data) return msg.say('Could not find any results.'); + const embed = new MessageEmbed() + .setColor(0xED1C24) + .setTitle(data.name) + .setDescription(data.description || 'No description available.'); + return msg.embed(embed); } }; diff --git a/commands/pokedex/pokedex-cry.js b/commands/pokedex/pokedex-cry.js index 0e6611b9..b075682c 100644 --- a/commands/pokedex/pokedex-cry.js +++ b/commands/pokedex/pokedex-cry.js @@ -67,7 +67,7 @@ module.exports = class PokedexCryCommand extends Command { return null; } catch (err) { await reactIfAble(msg, this.client.user, '⚠️'); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } }; diff --git a/commands/pokedex/pokedex-image.js b/commands/pokedex/pokedex-image.js index c6b6864b..505424d7 100644 --- a/commands/pokedex/pokedex-image.js +++ b/commands/pokedex/pokedex-image.js @@ -46,10 +46,6 @@ module.exports = class PokedexImageCommand extends Command { } run(msg, { pokemon }) { - try { - return msg.say(`#${pokemon.displayID} - ${pokemon.name}`, { files: [pokemon.spriteImageURL] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + return msg.say(`#${pokemon.displayID} - ${pokemon.name}`, { files: [pokemon.spriteImageURL] }); } }; diff --git a/commands/pokedex/pokedex-item.js b/commands/pokedex/pokedex-item.js index d635536f..ecee091f 100644 --- a/commands/pokedex/pokedex-item.js +++ b/commands/pokedex/pokedex-item.js @@ -32,18 +32,14 @@ module.exports = class PokedexItemCommand extends Command { } async run(msg, { item }) { - try { - const data = await this.client.pokemon.items.fetch(item); - if (!data) return msg.say('Could not find any results.'); - const embed = new MessageEmbed() - .setColor(0xED1C24) - .setTitle(data.name) - .setDescription(data.description || 'No description available.') - .setThumbnail(data.spriteURL) - .addField('❯ Cost', `${data.cost} ¥`, true); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const data = await this.client.pokemon.items.fetch(item); + if (!data) return msg.say('Could not find any results.'); + const embed = new MessageEmbed() + .setColor(0xED1C24) + .setTitle(data.name) + .setDescription(data.description || 'No description available.') + .setThumbnail(data.spriteURL) + .addField('❯ Cost', `${data.cost} ¥`, true); + return msg.embed(embed); } }; diff --git a/commands/pokedex/pokedex-location.js b/commands/pokedex/pokedex-location.js index a66fde0e..40f8a8a8 100644 --- a/commands/pokedex/pokedex-location.js +++ b/commands/pokedex/pokedex-location.js @@ -54,28 +54,24 @@ module.exports = class PokedexLocationCommand extends Command { } async run(msg, { pokemon }) { - try { - if (!pokemon.gameDataCached) await pokemon.fetchGameData(); - if (!pokemon.encounters) await pokemon.fetchEncounters(); - const desc = pokemon.encounters.length - ? pokemon.encounters - .map(location => `${location.name} (${location.versions.map(v => versions[v]).join('/')})`) - .join('\n') - : 'Location Unknown'; - const embed = new MessageEmbed() - .setColor(0xED1C24) - .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, 'attachment://box.png', pokemon.serebiiURL) - .setDescription(desc) - .setThumbnail(pokemon.spriteImageURL); - return msg.channel.send({ - embeds: [embed], - files: [{ - attachment: await pokemon.generateBoxImage(), - name: 'box.png' - }] - }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + if (!pokemon.gameDataCached) await pokemon.fetchGameData(); + if (!pokemon.encounters) await pokemon.fetchEncounters(); + const desc = pokemon.encounters.length + ? pokemon.encounters + .map(location => `${location.name} (${location.versions.map(v => versions[v]).join('/')})`) + .join('\n') + : 'Location Unknown'; + const embed = new MessageEmbed() + .setColor(0xED1C24) + .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, 'attachment://box.png', pokemon.serebiiURL) + .setDescription(desc) + .setThumbnail(pokemon.spriteImageURL); + return msg.channel.send({ + embeds: [embed], + files: [{ + attachment: await pokemon.generateBoxImage(), + name: 'box.png' + }] + }); } }; diff --git a/commands/pokedex/pokedex-move.js b/commands/pokedex/pokedex-move.js index 2ffd1fc8..dd8aa47c 100644 --- a/commands/pokedex/pokedex-move.js +++ b/commands/pokedex/pokedex-move.js @@ -32,22 +32,18 @@ module.exports = class PokedexMoveCommand extends Command { } async run(msg, { move }) { - try { - const data = await this.client.pokemon.moves.fetch(move); - if (!data) return msg.say('Could not find any results.'); - const embed = new MessageEmbed() - .setColor(0xED1C24) - .setTitle(data.name) - .setDescription(data.description ? data.cleanDescription : 'No description available.') - .addField('❯ Accuracy', `${data.accuracy}%`, true) - .addField('❯ Power', data.power.toString() || '???', true) - .addField('❯ PP', data.pp.toString(), true) - .addField('❯ Type', data.type.toString(), true) - .addField('❯ Contest Type', data.contestType || 'N/A', true) - .addField('❯ Class', data.class, true); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const data = await this.client.pokemon.moves.fetch(move); + if (!data) return msg.say('Could not find any results.'); + const embed = new MessageEmbed() + .setColor(0xED1C24) + .setTitle(data.name) + .setDescription(data.description ? data.cleanDescription : 'No description available.') + .addField('❯ Accuracy', `${data.accuracy}%`, true) + .addField('❯ Power', data.power.toString() || '???', true) + .addField('❯ PP', data.pp.toString(), true) + .addField('❯ Type', data.type.toString(), true) + .addField('❯ Contest Type', data.contestType || 'N/A', true) + .addField('❯ Class', data.class, true); + return msg.embed(embed); } }; diff --git a/commands/pokedex/pokedex-moveset.js b/commands/pokedex/pokedex-moveset.js index 7eaca017..00c5a40f 100644 --- a/commands/pokedex/pokedex-moveset.js +++ b/commands/pokedex/pokedex-moveset.js @@ -59,24 +59,20 @@ module.exports = class PokedexMovesetCommand extends Command { } async run(msg, { pokemon }) { - try { - if (!pokemon.gameDataCached) await pokemon.fetchGameData(); - if (!pokemon.moveSet.length) return msg.say('This Pokémon\'s moves are not yet documented.'); - const embed = new MessageEmbed() - .setColor(0xED1C24) - .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, 'attachment://box.png', pokemon.serebiiURL) - .setDescription(pokemon.moveSet.map(move => `**Level ${move.level}:** ${move.move.name}`).join('\n')) - .setThumbnail(pokemon.spriteImageURL) - .setFooter(`Moveset data taken from ${versions[pokemon.moveSetVersion]}.`); - return msg.channel.send({ - embeds: [embed], - files: [{ - attachment: await pokemon.generateBoxImage(), - name: 'box.png' - }] - }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + if (!pokemon.gameDataCached) await pokemon.fetchGameData(); + if (!pokemon.moveSet.length) return msg.say('This Pokémon\'s moves are not yet documented.'); + const embed = new MessageEmbed() + .setColor(0xED1C24) + .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, 'attachment://box.png', pokemon.serebiiURL) + .setDescription(pokemon.moveSet.map(move => `**Level ${move.level}:** ${move.move.name}`).join('\n')) + .setThumbnail(pokemon.spriteImageURL) + .setFooter(`Moveset data taken from ${versions[pokemon.moveSetVersion]}.`); + return msg.channel.send({ + embeds: [embed], + files: [{ + attachment: await pokemon.generateBoxImage(), + name: 'box.png' + }] + }); } }; diff --git a/commands/pokedex/pokedex-stats.js b/commands/pokedex/pokedex-stats.js index 6e610044..3f314eea 100644 --- a/commands/pokedex/pokedex-stats.js +++ b/commands/pokedex/pokedex-stats.js @@ -54,61 +54,57 @@ module.exports = class PokedexCommand extends Command { } async run(msg, { pokemon, form }) { - try { - if (!pokemon.gameDataCached) await pokemon.fetchGameData(); - const displayForms = pokemon.varieties.filter(vrity => vrity.statsDiffer); - const variety = displayForms.find(vrity => { - if (!form || form === 'normal') return vrity.default; - if (!vrity.name) return false; - return vrity.name.toLowerCase() === form; - }); - if (!variety) { - const varieties = displayForms.map(vrity => vrity.name || 'Normal'); - return msg.say(`Invalid form. The forms available for this Pokémon are: ${list(varieties, 'and')}`); - } - const statTotal = pokemon.baseStatTotal(variety.id); - const repeat = { - hp: Math.round((variety.stats.hp / 255) * 10) * 2, - atk: Math.round((variety.stats.atk / 255) * 10) * 2, - def: Math.round((variety.stats.def / 255) * 10) * 2, - sAtk: Math.round((variety.stats.sAtk / 255) * 10) * 2, - sDef: Math.round((variety.stats.sDef / 255) * 10) * 2, - spd: Math.round((variety.stats.spd / 255) * 10) * 2, - total: Math.round((statTotal / 1125) * 10) * 2 - }; - const embed = new MessageEmbed() - .setColor(0xED1C24) - .setAuthor( - `#${pokemon.displayID} - ${pokemon.name}`, - 'attachment://box.png', - pokemon.serebiiURL - ) - .setThumbnail(pokemon.formSpriteImageURL(variety.id)) - .addField(`❯ Base Stats (${variety.name || 'Normal'} Form)`, stripIndents` - \`HP: [${'█'.repeat(repeat.hp)}${' '.repeat(20 - repeat.hp)}]\` **${variety.stats.hp}** - \`Attack: [${'█'.repeat(repeat.atk)}${' '.repeat(20 - repeat.atk)}]\` **${variety.stats.atk}** - \`Defense: [${'█'.repeat(repeat.def)}${' '.repeat(20 - repeat.def)}]\` **${variety.stats.def}** - \`Sp. Attack: [${'█'.repeat(repeat.sAtk)}${' '.repeat(20 - repeat.sAtk)}]\` **${variety.stats.sAtk}** - \`Sp. Defense: [${'█'.repeat(repeat.sDef)}${' '.repeat(20 - repeat.sDef)}]\` **${variety.stats.sDef}** - \`Speed: [${'█'.repeat(repeat.spd)}${' '.repeat(20 - repeat.spd)}]\` **${variety.stats.spd}** - \`-----------------------------------\` - \`Total: [${'█'.repeat(repeat.total)}${' '.repeat(20 - repeat.total)}]\` **${statTotal}** - `) - .addField('❯ Abilities', variety.abilities.map(ability => ability.name).join('/')) - .addField('❯ Other Forms', stripIndents` - _Use ${this.usage(`${pokemon.id}
`)} to get stats for another form._ - - **Forms Available:** ${displayForms.map(vrity => `\`${vrity.name || 'Normal'}\``).join(', ')} - `); - return msg.channel.send({ - embeds: [embed], - files: [{ - attachment: await pokemon.generateBoxImage(), - name: 'box.png' - }] - }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + if (!pokemon.gameDataCached) await pokemon.fetchGameData(); + const displayForms = pokemon.varieties.filter(vrity => vrity.statsDiffer); + const variety = displayForms.find(vrity => { + if (!form || form === 'normal') return vrity.default; + if (!vrity.name) return false; + return vrity.name.toLowerCase() === form; + }); + if (!variety) { + const varieties = displayForms.map(vrity => vrity.name || 'Normal'); + return msg.say(`Invalid form. The forms available for this Pokémon are: ${list(varieties, 'and')}`); } + const statTotal = pokemon.baseStatTotal(variety.id); + const repeat = { + hp: Math.round((variety.stats.hp / 255) * 10) * 2, + atk: Math.round((variety.stats.atk / 255) * 10) * 2, + def: Math.round((variety.stats.def / 255) * 10) * 2, + sAtk: Math.round((variety.stats.sAtk / 255) * 10) * 2, + sDef: Math.round((variety.stats.sDef / 255) * 10) * 2, + spd: Math.round((variety.stats.spd / 255) * 10) * 2, + total: Math.round((statTotal / 1125) * 10) * 2 + }; + const embed = new MessageEmbed() + .setColor(0xED1C24) + .setAuthor( + `#${pokemon.displayID} - ${pokemon.name}`, + 'attachment://box.png', + pokemon.serebiiURL + ) + .setThumbnail(pokemon.formSpriteImageURL(variety.id)) + .addField(`❯ Base Stats (${variety.name || 'Normal'} Form)`, stripIndents` + \`HP: [${'█'.repeat(repeat.hp)}${' '.repeat(20 - repeat.hp)}]\` **${variety.stats.hp}** + \`Attack: [${'█'.repeat(repeat.atk)}${' '.repeat(20 - repeat.atk)}]\` **${variety.stats.atk}** + \`Defense: [${'█'.repeat(repeat.def)}${' '.repeat(20 - repeat.def)}]\` **${variety.stats.def}** + \`Sp. Attack: [${'█'.repeat(repeat.sAtk)}${' '.repeat(20 - repeat.sAtk)}]\` **${variety.stats.sAtk}** + \`Sp. Defense: [${'█'.repeat(repeat.sDef)}${' '.repeat(20 - repeat.sDef)}]\` **${variety.stats.sDef}** + \`Speed: [${'█'.repeat(repeat.spd)}${' '.repeat(20 - repeat.spd)}]\` **${variety.stats.spd}** + \`-----------------------------------\` + \`Total: [${'█'.repeat(repeat.total)}${' '.repeat(20 - repeat.total)}]\` **${statTotal}** + `) + .addField('❯ Abilities', variety.abilities.map(ability => ability.name).join('/')) + .addField('❯ Other Forms', stripIndents` + _Use ${this.usage(`${pokemon.id} `)} to get stats for another form._ + + **Forms Available:** ${displayForms.map(vrity => `\`${vrity.name || 'Normal'}\``).join(', ')} + `); + return msg.channel.send({ + embeds: [embed], + files: [{ + attachment: await pokemon.generateBoxImage(), + name: 'box.png' + }] + }); } }; diff --git a/commands/pokedex/pokedex.js b/commands/pokedex/pokedex.js index 31e2722a..8645043c 100644 --- a/commands/pokedex/pokedex.js +++ b/commands/pokedex/pokedex.js @@ -76,66 +76,62 @@ module.exports = class PokedexCommand extends Command { } async run(msg, { pokemon }) { - try { - if (!pokemon.gameDataCached) await pokemon.fetchGameData(); - const defaultVariety = pokemon.varieties.find(variety => variety.default); - const typesShown = pokemon.varieties.filter(variety => { - if (variety.default) return true; - return !arrayEquals(defaultVariety.types, variety.types); - }); - const feet = Math.floor(pokemon.height / 12); - const evoChain = pokemon.chain.data.map(pkmn => { - if (Array.isArray(pkmn)) { - return pkmn.map(pkmn2 => { - const found = this.client.pokemon.get(pkmn2); - if (found.id === pokemon.id) return `**${found.name}**`; - return found.name; - }).join('/'); - } - const found = this.client.pokemon.get(pkmn); - if (found.id === pokemon.id) return `**${found.name}**`; - return found.name; - }).join(' -> '); - const embed = new MessageEmbed() - .setColor(0xED1C24) - .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, 'attachment://box.png', pokemon.serebiiURL) - .setDescription(stripIndents` - **${pokemon.genus}** - ${pokemon.entries.length ? pokemon.entries[Math.floor(Math.random() * pokemon.entries.length)] : 'No data.'} - `) - .setThumbnail(pokemon.spriteImageURL) - .addField('❯ Introduced In', games[genGames[pokemon.generation]], true) - .addField('❯ Height', `${feet}'${Math.floor(pokemon.height) - (feet * 12)}"`, true) - .addField('❯ Weight', `${pokemon.weight} lbs.`, true) - .addField('❯ Types', typesShown.map(variety => { - const showParens = variety.name && typesShown.length > 1; - return `${variety.types.join('/')}${showParens ? ` (${variety.name})` : ''}`; - }).join('\n'), true) - .addField('❯ Class', firstUpperCase(pokemon.class), true) - .addField('❯ Gender Rate', pokemon.genderRate.genderless - ? 'Genderless' - : `♂️ ${pokemon.genderRate.male}% ♀️ ${pokemon.genderRate.female}%`, true) - .addField('❯ Evolution Chain', `${evoChain}${pokemon.mega ? ` -> ${this.megaEvolveEmoji}` : ''}`) - .addField('❯ Held Items', pokemon.heldItems.length - ? pokemon.heldItems.map(item => `${item.data.name} (${item.rarity}%)`).join('\n') - : 'None'); - if (msg.guild && pokemon.cry) { - const connection = msg.guild ? this.client.dispatchers.get(msg.guild.id) : null; - if (connection) { - connection.play(pokemon.cry); - await reactIfAble(msg, this.client.user, '🔉'); - } + if (!pokemon.gameDataCached) await pokemon.fetchGameData(); + const defaultVariety = pokemon.varieties.find(variety => variety.default); + const typesShown = pokemon.varieties.filter(variety => { + if (variety.default) return true; + return !arrayEquals(defaultVariety.types, variety.types); + }); + const feet = Math.floor(pokemon.height / 12); + const evoChain = pokemon.chain.data.map(pkmn => { + if (Array.isArray(pkmn)) { + return pkmn.map(pkmn2 => { + const found = this.client.pokemon.get(pkmn2); + if (found.id === pokemon.id) return `**${found.name}**`; + return found.name; + }).join('/'); + } + const found = this.client.pokemon.get(pkmn); + if (found.id === pokemon.id) return `**${found.name}**`; + return found.name; + }).join(' -> '); + const embed = new MessageEmbed() + .setColor(0xED1C24) + .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, 'attachment://box.png', pokemon.serebiiURL) + .setDescription(stripIndents` + **${pokemon.genus}** + ${pokemon.entries.length ? pokemon.entries[Math.floor(Math.random() * pokemon.entries.length)] : 'No data.'} + `) + .setThumbnail(pokemon.spriteImageURL) + .addField('❯ Introduced In', games[genGames[pokemon.generation]], true) + .addField('❯ Height', `${feet}'${Math.floor(pokemon.height) - (feet * 12)}"`, true) + .addField('❯ Weight', `${pokemon.weight} lbs.`, true) + .addField('❯ Types', typesShown.map(variety => { + const showParens = variety.name && typesShown.length > 1; + return `${variety.types.join('/')}${showParens ? ` (${variety.name})` : ''}`; + }).join('\n'), true) + .addField('❯ Class', firstUpperCase(pokemon.class), true) + .addField('❯ Gender Rate', pokemon.genderRate.genderless + ? 'Genderless' + : `♂️ ${pokemon.genderRate.male}% ♀️ ${pokemon.genderRate.female}%`, true) + .addField('❯ Evolution Chain', `${evoChain}${pokemon.mega ? ` -> ${this.megaEvolveEmoji}` : ''}`) + .addField('❯ Held Items', pokemon.heldItems.length + ? pokemon.heldItems.map(item => `${item.data.name} (${item.rarity}%)`).join('\n') + : 'None'); + if (msg.guild && pokemon.cry) { + const connection = msg.guild ? this.client.dispatchers.get(msg.guild.id) : null; + if (connection) { + connection.play(pokemon.cry); + await reactIfAble(msg, this.client.user, '🔉'); } - return msg.channel.send({ - embeds: [embed], - files: [{ - attachment: await pokemon.generateBoxImage(), - name: 'box.png' - }] - }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } + return msg.channel.send({ + embeds: [embed], + files: [{ + attachment: await pokemon.generateBoxImage(), + name: 'box.png' + }] + }); } get megaEvolveEmoji() { diff --git a/commands/pokedex/smogon.js b/commands/pokedex/smogon.js index 983211e3..62eaba40 100644 --- a/commands/pokedex/smogon.js +++ b/commands/pokedex/smogon.js @@ -60,31 +60,26 @@ module.exports = class SmogonCommand extends Command { } async run(msg, { pokemon }) { - try { - const fetchGames = genGames.slice(pokemon.generation, pokemon.missingno ? 2 : genGames.length); - if (!pokemon.missingno) await pokemon.fetchSmogonTiers(...fetchGames); - const embed = new MessageEmbed() - .setColor(0xED1C24) - .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, 'attachment://box.png', pokemon.serebiiURL) - .setThumbnail(pokemon.spriteImageURL); - for (const game of fetchGames) { - embed.addField(`❯ ${games[game]}`, - `[${pokemon.smogonTiers[game].join('/')}](${pokemon.smogonURL(game)})`, true); - } - if (fetchGames.length % 3 !== 0 && fetchGames.length > 3) { - for (let i = 0; i < 3 - (fetchGames.length % 3); i++) { - embed.addField('\u200B', '\u200B', true); - } - } - return msg.channel.send({ - embeds: [embed], - files: [{ - attachment: await pokemon.generateBoxImage(), - name: 'box.png' - }] - }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const fetchGames = genGames.slice(pokemon.generation, pokemon.missingno ? 2 : genGames.length); + if (!pokemon.missingno) await pokemon.fetchSmogonTiers(...fetchGames); + const embed = new MessageEmbed() + .setColor(0xED1C24) + .setAuthor(`#${pokemon.displayID} - ${pokemon.name}`, 'attachment://box.png', pokemon.serebiiURL) + .setThumbnail(pokemon.spriteImageURL); + for (const game of fetchGames) { + embed.addField(`❯ ${games[game]}`, `[${pokemon.smogonTiers[game].join('/')}](${pokemon.smogonURL(game)})`, true); } + if (fetchGames.length % 3 !== 0 && fetchGames.length > 3) { + for (let i = 0; i < 3 - (fetchGames.length % 3); i++) { + embed.addField('\u200B', '\u200B', true); + } + } + return msg.channel.send({ + embeds: [embed], + files: [{ + attachment: await pokemon.generateBoxImage(), + name: 'box.png' + }] + }); } }; diff --git a/commands/random-img/bird.js b/commands/random-img/bird.js index 46b0dd6d..27e223b9 100644 --- a/commands/random-img/bird.js +++ b/commands/random-img/bird.js @@ -21,17 +21,13 @@ module.exports = class BirdCommand extends Command { } async run(msg) { - try { - const { body } = await request - .get('https://shibe.online/api/birds') - .query({ - count: 1, - urls: true, - httpsUrls: true - }); - return msg.say({ files: [body[0]] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get('https://shibe.online/api/birds') + .query({ + count: 1, + urls: true, + httpsUrls: true + }); + return msg.say({ files: [body[0]] }); } }; diff --git a/commands/random-img/bunny.js b/commands/random-img/bunny.js index b4669c3b..d33f4cb2 100644 --- a/commands/random-img/bunny.js +++ b/commands/random-img/bunny.js @@ -22,25 +22,21 @@ module.exports = class BunnyCommand extends Command { } async run(msg) { - try { - const { body } = await request - .get('https://api.bunnies.io/v2/loop/random/') - .query({ media: 'gif,png' }); - let fileToSend; - let fileType = 'gif'; - const gif = await request.get(body.media.gif); - if (Buffer.byteLength(gif.body) > 8e+6) { - const poster = await request.get(body.media.poster); - fileToSend = poster.body; - fileType = 'png'; - } else { - fileToSend = gif.body; - } - return msg.say(facts[Math.floor(Math.random() * facts.length)], { - files: [{ attachment: fileToSend, name: `${body.id}.${fileType}` }] - }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const { body } = await request + .get('https://api.bunnies.io/v2/loop/random/') + .query({ media: 'gif,png' }); + let fileToSend; + let fileType = 'gif'; + const gif = await request.get(body.media.gif); + if (Buffer.byteLength(gif.body) > 8e+6) { + const poster = await request.get(body.media.poster); + fileToSend = poster.body; + fileType = 'png'; + } else { + fileToSend = gif.body; } + return msg.say(facts[Math.floor(Math.random() * facts.length)], { + files: [{ attachment: fileToSend, name: `${body.id}.${fileType}` }] + }); } }; diff --git a/commands/random-img/cat.js b/commands/random-img/cat.js index 4d1b7953..ea6c50f1 100644 --- a/commands/random-img/cat.js +++ b/commands/random-img/cat.js @@ -24,14 +24,10 @@ module.exports = class CatCommand extends Command { } async run(msg) { - try { - const { body } = await request - .get('https://api.thecatapi.com/v1/images/search') - .query({ limit: 1 }) - .set({ 'x-api-key': THECATAPI_KEY }); - return msg.say(facts[Math.floor(Math.random() * facts.length)], { files: [body[0].url] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get('https://api.thecatapi.com/v1/images/search') + .query({ limit: 1 }) + .set({ 'x-api-key': THECATAPI_KEY }); + return msg.say(facts[Math.floor(Math.random() * facts.length)], { files: [body[0].url] }); } }; diff --git a/commands/random-img/dog.js b/commands/random-img/dog.js index 211179fd..43a21acd 100644 --- a/commands/random-img/dog.js +++ b/commands/random-img/dog.js @@ -24,14 +24,10 @@ module.exports = class DogCommand extends Command { } async run(msg) { - try { - const { body } = await request - .get('https://api.thedogapi.com/v1/images/search') - .query({ limit: 1 }) - .set({ 'x-api-key': THEDOGAPI_KEY }); - return msg.say(facts[Math.floor(Math.random() * facts.length)], { files: [body[0].url] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get('https://api.thedogapi.com/v1/images/search') + .query({ limit: 1 }) + .set({ 'x-api-key': THEDOGAPI_KEY }); + return msg.say(facts[Math.floor(Math.random() * facts.length)], { files: [body[0].url] }); } }; diff --git a/commands/random-img/duck.js b/commands/random-img/duck.js index 5a20473f..6b934a78 100644 --- a/commands/random-img/duck.js +++ b/commands/random-img/duck.js @@ -22,11 +22,7 @@ module.exports = class DuckCommand extends Command { } async run(msg) { - try { - const { body } = await request.get('https://random-d.uk/api/v1/random'); - return msg.say({ files: [body.url] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request.get('https://random-d.uk/api/v1/random'); + return msg.say({ files: [body.url] }); } }; diff --git a/commands/random-img/fox.js b/commands/random-img/fox.js index 4cb28cb9..9f5e525d 100644 --- a/commands/random-img/fox.js +++ b/commands/random-img/fox.js @@ -20,11 +20,7 @@ module.exports = class FoxCommand extends Command { } async run(msg) { - try { - const { body } = await request.get('https://randomfox.ca/floof/'); - return msg.say({ files: [body.image] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request.get('https://randomfox.ca/floof/'); + return msg.say({ files: [body.image] }); } }; diff --git a/commands/random-img/goose.js b/commands/random-img/goose.js index 2eed1c71..e76f774f 100644 --- a/commands/random-img/goose.js +++ b/commands/random-img/goose.js @@ -21,11 +21,7 @@ module.exports = class GooseCommand extends Command { } async run(msg) { - try { - const { body } = await request.get('https://nekos.life/api/v2/img/goose'); - return msg.say({ files: [body.url] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request.get('https://nekos.life/api/v2/img/goose'); + return msg.say({ files: [body.url] }); } }; diff --git a/commands/random-img/inspiration.js b/commands/random-img/inspiration.js index 1e789bf2..945d7d8c 100644 --- a/commands/random-img/inspiration.js +++ b/commands/random-img/inspiration.js @@ -21,13 +21,9 @@ module.exports = class InspirationCommand extends Command { } async run(msg) { - try { - const { text } = await request - .get('https://inspirobot.me/api') - .query({ generate: true }); - return msg.say({ files: [text] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { text } = await request + .get('https://inspirobot.me/api') + .query({ generate: true }); + return msg.say({ files: [text] }); } }; diff --git a/commands/random-img/light-novel-cover.js b/commands/random-img/light-novel-cover.js index 36b26af0..64b915b0 100644 --- a/commands/random-img/light-novel-cover.js +++ b/commands/random-img/light-novel-cover.js @@ -22,15 +22,11 @@ module.exports = class LightNovelCoverCommand extends Command { } async run(msg) { - try { - const { text } = await request.get('https://salty-salty-studios.com/shiz/lncovers.php'); - const $ = cheerio.load(text); - const cover = $('img').first(); - return msg.say(cover.attr('alt'), { - files: [`https://salty-salty-studios.com/shiz/${cover.attr('src')}`] - }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { text } = await request.get('https://salty-salty-studios.com/shiz/lncovers.php'); + const $ = cheerio.load(text); + const cover = $('img').first(); + return msg.say(cover.attr('alt'), { + files: [`https://salty-salty-studios.com/shiz/${cover.attr('src')}`] + }); } }; diff --git a/commands/random-img/lizard.js b/commands/random-img/lizard.js index 70edb8a5..960d139f 100644 --- a/commands/random-img/lizard.js +++ b/commands/random-img/lizard.js @@ -20,11 +20,7 @@ module.exports = class LizardCommand extends Command { } async run(msg) { - try { - const { body } = await request.get('https://nekos.life/api/v2/img/lizard'); - return msg.say({ files: [body.url] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request.get('https://nekos.life/api/v2/img/lizard'); + return msg.say({ files: [body.url] }); } }; diff --git a/commands/random-img/lorem-picsum.js b/commands/random-img/lorem-picsum.js index e4bc1a6c..f730febd 100644 --- a/commands/random-img/lorem-picsum.js +++ b/commands/random-img/lorem-picsum.js @@ -42,11 +42,7 @@ module.exports = class LoremPicsumCommand extends Command { } async run(msg, { width, height, seed }) { - try { - const { body } = await request.get(`https://picsum.photos/${seed ? `seed/${seed}/` : ''}${width}/${height}`); - return msg.say({ files: [{ attachment: body, name: `${width}x${height}.jpg` }] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request.get(`https://picsum.photos/${seed ? `seed/${seed}/` : ''}${width}/${height}`); + return msg.say({ files: [{ attachment: body, name: `${width}x${height}.jpg` }] }); } }; diff --git a/commands/random-img/shiba.js b/commands/random-img/shiba.js index 488d16f9..0ed3dfe8 100644 --- a/commands/random-img/shiba.js +++ b/commands/random-img/shiba.js @@ -21,17 +21,13 @@ module.exports = class ShibaCommand extends Command { } async run(msg) { - try { - const { body } = await request - .get('https://shibe.online/api/shibes') - .query({ - count: 1, - urls: true, - httpsUrls: true - }); - return msg.say({ files: [body[0]] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get('https://shibe.online/api/shibes') + .query({ + count: 1, + urls: true, + httpsUrls: true + }); + return msg.say({ files: [body[0]] }); } }; diff --git a/commands/random-res/advice.js b/commands/random-res/advice.js index 09ccf9c4..354ebec0 100644 --- a/commands/random-res/advice.js +++ b/commands/random-res/advice.js @@ -21,12 +21,8 @@ module.exports = class AdviceCommand extends Command { } async run(msg) { - try { - const { text } = await request.get('http://api.adviceslip.com/advice'); - const body = JSON.parse(text); - return msg.say(`${body.slip.advice} (#${body.slip.id})`); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { text } = await request.get('http://api.adviceslip.com/advice'); + const body = JSON.parse(text); + return msg.say(`${body.slip.advice} (#${body.slip.id})`); } }; diff --git a/commands/random-res/boredom.js b/commands/random-res/boredom.js index 8812044a..ea799553 100644 --- a/commands/random-res/boredom.js +++ b/commands/random-res/boredom.js @@ -20,11 +20,7 @@ module.exports = class BoredomCommand extends Command { } async run(msg) { - try { - const { body } = await request.get('https://www.boredapi.com/api/activity/'); - return msg.say(`${body.activity} (${body.type})`); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request.get('https://www.boredapi.com/api/activity/'); + return msg.say(`${body.activity} (${body.type})`); } }; diff --git a/commands/random-res/chuck-norris.js b/commands/random-res/chuck-norris.js index 06bca5f3..e4ff9761 100644 --- a/commands/random-res/chuck-norris.js +++ b/commands/random-res/chuck-norris.js @@ -26,11 +26,7 @@ module.exports = class ChuckNorrisCommand extends Command { } async run(msg) { - try { - const { body } = await request.get('https://api.chucknorris.io/jokes/random'); - return msg.say(body.value); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request.get('https://api.chucknorris.io/jokes/random'); + return msg.say(body.value); } }; diff --git a/commands/random-res/fact.js b/commands/random-res/fact.js index f131734d..072f0e0b 100644 --- a/commands/random-res/fact.js +++ b/commands/random-res/fact.js @@ -20,30 +20,26 @@ module.exports = class FactCommand extends Command { } async run(msg) { - try { - const article = await this.randomWikipediaArticle(); - const { body } = await request - .get('https://en.wikipedia.org/w/api.php') - .query({ - action: 'query', - prop: 'extracts', - format: 'json', - titles: article, - exintro: '', - explaintext: '', - redirects: '', - formatversion: 2 - }); - let fact = body.query.pages[0].extract; - if (fact.length > 200) { - const facts = fact.split('.'); - fact = `${facts[0]}.`; - if (fact.length < 200 && facts.length > 1) fact += `${facts[1]}.`; - } - return msg.say(fact); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const article = await this.randomWikipediaArticle(); + const { body } = await request + .get('https://en.wikipedia.org/w/api.php') + .query({ + action: 'query', + prop: 'extracts', + format: 'json', + titles: article, + exintro: '', + explaintext: '', + redirects: '', + formatversion: 2 + }); + let fact = body.query.pages[0].extract; + if (fact.length > 200) { + const facts = fact.split('.'); + fact = `${facts[0]}.`; + if (fact.length < 200 && facts.length > 1) fact += `${facts[1]}.`; } + return msg.say(fact); } async randomWikipediaArticle() { diff --git a/commands/random-res/fml.js b/commands/random-res/fml.js index 6b72143e..5430545d 100644 --- a/commands/random-res/fml.js +++ b/commands/random-res/fml.js @@ -22,13 +22,9 @@ module.exports = class FmlCommand extends Command { } async run(msg) { - try { - const { text } = await request.get('http://www.fmylife.com/random'); - const $ = cheerio.load(text, { normalizeWhitespace: true }); - const fml = $('a.block').first().text().trim(); - return msg.say(fml); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { text } = await request.get('http://www.fmylife.com/random'); + const $ = cheerio.load(text, { normalizeWhitespace: true }); + const fml = $('a.block').first().text().trim(); + return msg.say(fml); } }; diff --git a/commands/random-res/github-zen.js b/commands/random-res/github-zen.js index 06e44648..668f33fa 100644 --- a/commands/random-res/github-zen.js +++ b/commands/random-res/github-zen.js @@ -21,11 +21,7 @@ module.exports = class GithubZenCommand extends Command { } async run(msg) { - try { - const { text } = await request.get('https://api.github.com/zen'); - return msg.say(text); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { text } = await request.get('https://api.github.com/zen'); + return msg.say(text); } }; diff --git a/commands/random-res/joke.js b/commands/random-res/joke.js index 73bcace1..cd20b086 100644 --- a/commands/random-res/joke.js +++ b/commands/random-res/joke.js @@ -23,19 +23,15 @@ module.exports = class JokeCommand extends Command { async run(msg) { const blacklist = msg.channel.nsfw ? blacklistFlags : blacklistFlags.concat(nsfw); - try { - const { body } = await request - .get('https://v2.jokeapi.dev/joke/Any') - .query({ blacklistFlags: blacklist.join(',') }); - if (body.type === 'twopart') { - return msg.say(stripIndents` - ${body.setup} - ${body.delivery} - `); - } - return msg.say(body.joke); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const { body } = await request + .get('https://v2.jokeapi.dev/joke/Any') + .query({ blacklistFlags: blacklist.join(',') }); + if (body.type === 'twopart') { + return msg.say(stripIndents` + ${body.setup} + ${body.delivery} + `); } + return msg.say(body.joke); } }; diff --git a/commands/random-res/light-novel-title.js b/commands/random-res/light-novel-title.js index 9732061d..7bd18b94 100644 --- a/commands/random-res/light-novel-title.js +++ b/commands/random-res/light-novel-title.js @@ -20,11 +20,7 @@ module.exports = class LightNovelTitleCommand extends Command { } async run(msg) { - try { - const { text } = await request.get('https://salty-salty-studios.com/shiz/ln.php'); - return msg.say(text.match(/

(.+)<\/h1>/i)[1]); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { text } = await request.get('https://salty-salty-studios.com/shiz/ln.php'); + return msg.say(text.match(/

(.+)<\/h1>/i)[1]); } }; diff --git a/commands/random-res/mtg-card.js b/commands/random-res/mtg-card.js index 5e10c13a..78890d3e 100644 --- a/commands/random-res/mtg-card.js +++ b/commands/random-res/mtg-card.js @@ -21,11 +21,7 @@ module.exports = class MtgCardCommand extends Command { } async run(msg) { - try { - const { url } = await request.get('https://scryfall.com/random?q=is%3Aspell+game%3Apaper'); - return msg.say(url); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { url } = await request.get('https://scryfall.com/random?q=is%3Aspell+game%3Apaper'); + return msg.say(url); } }; diff --git a/commands/random-res/name.js b/commands/random-res/name.js index fa0fada6..9e4daedb 100644 --- a/commands/random-res/name.js +++ b/commands/random-res/name.js @@ -30,19 +30,15 @@ module.exports = class NameCommand extends Command { } async run(msg, { gender }) { - try { - const { body } = await request - .get('https://randomuser.me/api/') - .query({ - inc: 'name', - noinfo: '', - gender: gender === 'both' ? '' : gender, - nat: 'AU,US,CA,GB' - }); - const data = body.results[0].name; - return msg.say(`${data.first} ${data.last}`); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get('https://randomuser.me/api/') + .query({ + inc: 'name', + noinfo: '', + gender: gender === 'both' ? '' : gender, + nat: 'AU,US,CA,GB' + }); + const data = body.results[0].name; + return msg.say(`${data.first} ${data.last}`); } }; diff --git a/commands/random-res/number-fact.js b/commands/random-res/number-fact.js index 0c06efe1..549a1851 100644 --- a/commands/random-res/number-fact.js +++ b/commands/random-res/number-fact.js @@ -31,7 +31,7 @@ module.exports = class NumberFactCommand extends Command { return msg.say(text); } catch (err) { if (err.status === 404) return msg.say('Could not find any results.'); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } }; diff --git a/commands/random-res/pun.js b/commands/random-res/pun.js index a9848e19..93823dfe 100644 --- a/commands/random-res/pun.js +++ b/commands/random-res/pun.js @@ -23,19 +23,15 @@ module.exports = class PunCommand extends Command { async run(msg) { const blacklist = msg.channel.nsfw ? blacklistFlags : blacklistFlags.concat(nsfw); - try { - const { body } = await request - .get('https://v2.jokeapi.dev/joke/Pun') - .query({ blacklistFlags: blacklist.join(',') }); - if (body.type === 'twopart') { - return msg.say(stripIndents` - ${body.setup} - ${body.delivery} - `); - } - return msg.say(body.joke); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const { body } = await request + .get('https://v2.jokeapi.dev/joke/Pun') + .query({ blacklistFlags: blacklist.join(',') }); + if (body.type === 'twopart') { + return msg.say(stripIndents` + ${body.setup} + ${body.delivery} + `); } + return msg.say(body.joke); } }; diff --git a/commands/random-res/superpower.js b/commands/random-res/superpower.js index f5956b98..86f25325 100644 --- a/commands/random-res/superpower.js +++ b/commands/random-res/superpower.js @@ -26,16 +26,12 @@ module.exports = class SuperpowerCommand extends Command { } async run(msg) { - try { - const id = await this.random(); - const article = await this.fetchSuperpower(id); - return msg.reply(stripIndents` - Your superpower is... **${article.title}**! - _${article.abstract.split('1')[0].trim()}_ - `); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const id = await this.random(); + const article = await this.fetchSuperpower(id); + return msg.reply(stripIndents` + Your superpower is... **${article.title}**! + _${article.abstract.split('1')[0].trim()}_ + `); } async random() { diff --git a/commands/random-seed/friendship.js b/commands/random-seed/friendship.js index bc00ffc9..e8379624 100644 --- a/commands/random-seed/friendship.js +++ b/commands/random-seed/friendship.js @@ -65,35 +65,31 @@ module.exports = class FriendshipCommand extends Command { } const firstAvatarURL = first.displayAvatarURL({ format: 'png', size: 512 }); const secondAvatarURL = second.displayAvatarURL({ format: 'png', size: 512 }); - try { - const firstAvatarData = await request.get(firstAvatarURL); - const firstAvatar = await loadImage(firstAvatarData.body); - const secondAvatarData = await request.get(secondAvatarURL); - const secondAvatar = await loadImage(secondAvatarData.body); - const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'friendship.png')); - const canvas = createCanvas(base.width, base.height); - const ctx = canvas.getContext('2d'); - ctx.drawImage(firstAvatar, 70, 56, 400, 400); - ctx.drawImage(secondAvatar, 730, 56, 400, 400); - ctx.drawImage(base, 0, 0); - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - ctx.fillStyle = 'green'; - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(40); - ctx.fillText('~Xiao\'s Friendship Meter~', 600, 15); - ctx.fillStyle = 'white'; - ctx.fillText(first.username, 270, 448); - ctx.fillText(second.username, 930, 448); - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(60); - ctx.fillStyle = percentColor(level / 100, percentColors); - ctx.fillText(`~${level}%~`, 600, 230); - ctx.fillText(this.calculateLevelText(level, self, owner, authorUser, botUser), 600, 296); - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(90); - ctx.fillText(level > 49 ? '👍' : '👎', 600, 100); - return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'friendship.png' }] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const firstAvatarData = await request.get(firstAvatarURL); + const firstAvatar = await loadImage(firstAvatarData.body); + const secondAvatarData = await request.get(secondAvatarURL); + const secondAvatar = await loadImage(secondAvatarData.body); + const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'friendship.png')); + const canvas = createCanvas(base.width, base.height); + const ctx = canvas.getContext('2d'); + ctx.drawImage(firstAvatar, 70, 56, 400, 400); + ctx.drawImage(secondAvatar, 730, 56, 400, 400); + ctx.drawImage(base, 0, 0); + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + ctx.fillStyle = 'green'; + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(40); + ctx.fillText('~Xiao\'s Friendship Meter~', 600, 15); + ctx.fillStyle = 'white'; + ctx.fillText(first.username, 270, 448); + ctx.fillText(second.username, 930, 448); + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(60); + ctx.fillStyle = percentColor(level / 100, percentColors); + ctx.fillText(`~${level}%~`, 600, 230); + ctx.fillText(this.calculateLevelText(level, self, owner, authorUser, botUser), 600, 296); + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(90); + ctx.fillText(level > 49 ? '👍' : '👎', 600, 100); + return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'friendship.png' }] }); } calculateLevelText(level, self, owner, authorUser, botUser) { diff --git a/commands/random-seed/ship.js b/commands/random-seed/ship.js index 6a22df30..9db8b122 100644 --- a/commands/random-seed/ship.js +++ b/commands/random-seed/ship.js @@ -68,35 +68,31 @@ module.exports = class ShipCommand extends Command { } const firstAvatarURL = first.displayAvatarURL({ format: 'png', size: 512 }); const secondAvatarURL = second.displayAvatarURL({ format: 'png', size: 512 }); - try { - const firstAvatarData = await request.get(firstAvatarURL); - const firstAvatar = await loadImage(firstAvatarData.body); - const secondAvatarData = await request.get(secondAvatarURL); - const secondAvatar = await loadImage(secondAvatarData.body); - const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'ship.png')); - const canvas = createCanvas(base.width, base.height); - const ctx = canvas.getContext('2d'); - ctx.drawImage(firstAvatar, 70, 56, 400, 400); - ctx.drawImage(secondAvatar, 730, 56, 400, 400); - ctx.drawImage(base, 0, 0); - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - ctx.fillStyle = '#ff6c6c'; - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(40); - ctx.fillText('~Xiao\'s Compatability Meter~', 600, 15); - ctx.fillStyle = 'white'; - ctx.fillText(first.username, 270, 448); - ctx.fillText(second.username, 930, 448); - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(60); - ctx.fillStyle = percentColor(level / 100, percentColors); - ctx.fillText(`~${level}%~`, 600, 230); - ctx.fillText(this.calculateLevelText(level, self, owner, authorUser, botUser), 600, 296); - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(90); - ctx.fillText(level > 49 ? '❤️' : '💔', 600, 100); - return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'ship.png' }] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const firstAvatarData = await request.get(firstAvatarURL); + const firstAvatar = await loadImage(firstAvatarData.body); + const secondAvatarData = await request.get(secondAvatarURL); + const secondAvatar = await loadImage(secondAvatarData.body); + const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'ship.png')); + const canvas = createCanvas(base.width, base.height); + const ctx = canvas.getContext('2d'); + ctx.drawImage(firstAvatar, 70, 56, 400, 400); + ctx.drawImage(secondAvatar, 730, 56, 400, 400); + ctx.drawImage(base, 0, 0); + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + ctx.fillStyle = '#ff6c6c'; + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(40); + ctx.fillText('~Xiao\'s Compatability Meter~', 600, 15); + ctx.fillStyle = 'white'; + ctx.fillText(first.username, 270, 448); + ctx.fillText(second.username, 930, 448); + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(60); + ctx.fillStyle = percentColor(level / 100, percentColors); + ctx.fillText(`~${level}%~`, 600, 230); + ctx.fillText(this.calculateLevelText(level, self, owner, authorUser, botUser), 600, 296); + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(90); + ctx.fillText(level > 49 ? '❤️' : '💔', 600, 100); + return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'ship.png' }] }); } calculateLevelText(level, self, owner, authorUser, botUser) { diff --git a/commands/random-seed/think-of.js b/commands/random-seed/think-of.js index 0bbf7381..b88b3589 100644 --- a/commands/random-seed/think-of.js +++ b/commands/random-seed/think-of.js @@ -62,35 +62,31 @@ module.exports = class ThinkOfCommand extends Command { } const firstAvatarURL = first.displayAvatarURL({ format: 'png', size: 512 }); const secondAvatarURL = second.displayAvatarURL({ format: 'png', size: 512 }); - try { - const firstAvatarData = await request.get(firstAvatarURL); - const firstAvatar = await loadImage(firstAvatarData.body); - const secondAvatarData = await request.get(secondAvatarURL); - const secondAvatar = await loadImage(secondAvatarData.body); - const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'friendship.png')); - const canvas = createCanvas(base.width, base.height); - const ctx = canvas.getContext('2d'); - ctx.drawImage(firstAvatar, 70, 56, 400, 400); - ctx.drawImage(secondAvatar, 730, 56, 400, 400); - ctx.drawImage(base, 0, 0); - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - ctx.fillStyle = 'green'; - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(40); - ctx.fillText('~Xiao\'s Thought Reader~', 600, 15); - ctx.fillStyle = 'white'; - ctx.fillText(first.username, 270, 448); - ctx.fillText(second.username, 930, 448); - ctx.fillStyle = thought.color; - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(40); - ctx.fillText('thinks they are', 600, 230); - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(60); - ctx.fillText(thought.text, 600, 296); - ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(90); - ctx.fillText(thought.emoji, 600, 100); - return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'think-of.png' }] }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const firstAvatarData = await request.get(firstAvatarURL); + const firstAvatar = await loadImage(firstAvatarData.body); + const secondAvatarData = await request.get(secondAvatarURL); + const secondAvatar = await loadImage(secondAvatarData.body); + const base = await loadImage(path.join(__dirname, '..', '..', 'assets', 'images', 'friendship.png')); + const canvas = createCanvas(base.width, base.height); + const ctx = canvas.getContext('2d'); + ctx.drawImage(firstAvatar, 70, 56, 400, 400); + ctx.drawImage(secondAvatar, 730, 56, 400, 400); + ctx.drawImage(base, 0, 0); + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + ctx.fillStyle = 'green'; + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(40); + ctx.fillText('~Xiao\'s Thought Reader~', 600, 15); + ctx.fillStyle = 'white'; + ctx.fillText(first.username, 270, 448); + ctx.fillText(second.username, 930, 448); + ctx.fillStyle = thought.color; + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(40); + ctx.fillText('thinks they are', 600, 230); + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(60); + ctx.fillText(thought.text, 600, 296); + ctx.font = this.client.fonts.get('Pinky Cupid.otf').toCanvasString(90); + ctx.fillText(thought.emoji, 600, 100); + return msg.say({ files: [{ attachment: canvas.toBuffer(), name: 'think-of.png' }] }); } }; diff --git a/commands/search/anilist.js b/commands/search/anilist.js index 3a80825e..ab275ceb 100644 --- a/commands/search/anilist.js +++ b/commands/search/anilist.js @@ -39,15 +39,11 @@ module.exports = class AnilistCommand extends Command { } async run(msg, { query }) { - try { - const data = await this.search(query); - if (!data || !data.id || !data.name) return msg.say('Could not find any results.'); - return msg.say(``, { - files: [{ attachment: `https://img.anili.st/user/${data.id}`, name: 'anilist.png' }] - }); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const data = await this.search(query); + if (!data || !data.id || !data.name) return msg.say('Could not find any results.'); + return msg.say(``, { + files: [{ attachment: `https://img.anili.st/user/${data.id}`, name: 'anilist.png' }] + }); } async search(query) { diff --git a/commands/search/anime-character.js b/commands/search/anime-character.js index 927bbdf0..e33f3eb9 100644 --- a/commands/search/anime-character.js +++ b/commands/search/anime-character.js @@ -71,25 +71,21 @@ module.exports = class AnimeCharacterCommand extends Command { } async run(msg, { query }) { - try { - const id = await this.search(query); - if (!id) return msg.say('Could not find any results.'); - const character = await this.fetchCharacter(id); - const embed = new MessageEmbed() - .setColor(0x02A9FF) - .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') - .setURL(character.siteUrl) - .setThumbnail(character.image.large || character.image.medium || null) - .setTitle(`${character.name.first || ''}${character.name.last ? ` ${character.name.last}` : ''}`) - .setDescription(character.description ? cleanAnilistHTML(character.description, false) : 'No description.') - .addField('❯ Appearances', trimArray(character.media.edges.map(edge => { - const title = edge.node.title.english || edge.node.title.romaji; - return embedURL(`${title} (${types[edge.node.type]})`, edge.node.siteUrl); - }), 5).join(', ')); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const id = await this.search(query); + if (!id) return msg.say('Could not find any results.'); + const character = await this.fetchCharacter(id); + const embed = new MessageEmbed() + .setColor(0x02A9FF) + .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') + .setURL(character.siteUrl) + .setThumbnail(character.image.large || character.image.medium || null) + .setTitle(`${character.name.first || ''}${character.name.last ? ` ${character.name.last}` : ''}`) + .setDescription(character.description ? cleanAnilistHTML(character.description, false) : 'No description.') + .addField('❯ Appearances', trimArray(character.media.edges.map(edge => { + const title = edge.node.title.english || edge.node.title.romaji; + return embedURL(`${title} (${types[edge.node.type]})`, edge.node.siteUrl); + }), 5).join(', ')); + return msg.embed(embed); } async search(query) { diff --git a/commands/search/anime-staff.js b/commands/search/anime-staff.js index 6599a4b8..9ed2acdd 100644 --- a/commands/search/anime-staff.js +++ b/commands/search/anime-staff.js @@ -91,31 +91,27 @@ module.exports = class AnimeStaffCommand extends Command { } async run(msg, { query }) { - try { - const id = await this.search(query); - if (!id) return msg.say('Could not find any results.'); - const staff = await this.fetchStaff(id); - const embed = new MessageEmbed() - .setColor(0x02A9FF) - .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') - .setURL(staff.siteUrl) - .setThumbnail(staff.image.large || staff.image.medium || null) - .setTitle(`${staff.name.first || ''}${staff.name.last ? ` ${staff.name.last}` : ''}`) - .setDescription(staff.description ? cleanAnilistHTML(staff.description, false) : 'No description.') - .addField('❯ Voice Roles', - staff.characterMedia.edges.length ? trimArray(staff.characterMedia.edges.map(edge => { - const title = edge.node.title.english || edge.node.title.romaji; - return embedURL(`${title} (${roles[edge.characterRole]})`, edge.node.siteUrl); - }), 5).join(', ') : 'None') - .addField('❯ Production Roles', - staff.staffMedia.edges.length ? trimArray(staff.staffMedia.edges.map(edge => { - const title = edge.node.title.english || edge.node.title.romaji; - return embedURL(`${title} (${types[edge.node.type]})`, edge.node.siteUrl); - }), 5).join(', ') : 'None'); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const id = await this.search(query); + if (!id) return msg.say('Could not find any results.'); + const staff = await this.fetchStaff(id); + const embed = new MessageEmbed() + .setColor(0x02A9FF) + .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') + .setURL(staff.siteUrl) + .setThumbnail(staff.image.large || staff.image.medium || null) + .setTitle(`${staff.name.first || ''}${staff.name.last ? ` ${staff.name.last}` : ''}`) + .setDescription(staff.description ? cleanAnilistHTML(staff.description, false) : 'No description.') + .addField('❯ Voice Roles', + staff.characterMedia.edges.length ? trimArray(staff.characterMedia.edges.map(edge => { + const title = edge.node.title.english || edge.node.title.romaji; + return embedURL(`${title} (${roles[edge.characterRole]})`, edge.node.siteUrl); + }), 5).join(', ') : 'None') + .addField('❯ Production Roles', + staff.staffMedia.edges.length ? trimArray(staff.staffMedia.edges.map(edge => { + const title = edge.node.title.english || edge.node.title.romaji; + return embedURL(`${title} (${types[edge.node.type]})`, edge.node.siteUrl); + }), 5).join(', ') : 'None'); + return msg.embed(embed); } async search(query) { diff --git a/commands/search/anime.js b/commands/search/anime.js index c9fca7e7..1ab5e5a3 100644 --- a/commands/search/anime.js +++ b/commands/search/anime.js @@ -110,34 +110,30 @@ module.exports = class AnimeCommand extends Command { } async run(msg, { query }) { - try { - const id = await this.search(query); - if (!id) return msg.say('Could not find any results.'); - const anime = await this.fetchAnime(id); - if (!this.personalList) await this.fetchPersonalList(); - const entry = this.personalList.find(ani => ani.mediaId === id); - const malScore = await this.fetchMALScore(anime.idMal); - const malURL = `https://myanimelist.net/anime/${anime.idMal}`; - const embed = new MessageEmbed() - .setColor(0x02A9FF) - .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') - .setURL(anime.siteUrl) - .setThumbnail(anime.coverImage.large || anime.coverImage.medium || null) - .setTitle(anime.title.english || anime.title.romaji) - .setDescription(anime.description ? cleanAnilistHTML(anime.description) : 'No description.') - .addField('❯ Status', statuses[anime.status], true) - .addField('❯ Episodes', anime.episodes?.toString() || '???', true) - .addField('❯ Season', anime.season ? `${seasons[anime.season]} ${anime.startDate.year}` : '???', true) - .addField('❯ Average Score', anime.averageScore ? `${anime.averageScore}%` : '???', true) - .addField(`❯ MAL Score`, malScore ? embedURL(malScore, malURL) : '???', true) - .addField(`❯ ${ANILIST_USERNAME}'s Score`, entry && entry.score ? `${entry.score}/10` : '???', true) - .addField('❯ External Links', anime.externalLinks.length - ? anime.externalLinks.map(link => `[${link.site}](${link.url})`).join(', ') - : 'None'); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const id = await this.search(query); + if (!id) return msg.say('Could not find any results.'); + const anime = await this.fetchAnime(id); + if (!this.personalList) await this.fetchPersonalList(); + const entry = this.personalList.find(ani => ani.mediaId === id); + const malScore = await this.fetchMALScore(anime.idMal); + const malURL = `https://myanimelist.net/anime/${anime.idMal}`; + const embed = new MessageEmbed() + .setColor(0x02A9FF) + .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') + .setURL(anime.siteUrl) + .setThumbnail(anime.coverImage.large || anime.coverImage.medium || null) + .setTitle(anime.title.english || anime.title.romaji) + .setDescription(anime.description ? cleanAnilistHTML(anime.description) : 'No description.') + .addField('❯ Status', statuses[anime.status], true) + .addField('❯ Episodes', anime.episodes?.toString() || '???', true) + .addField('❯ Season', anime.season ? `${seasons[anime.season]} ${anime.startDate.year}` : '???', true) + .addField('❯ Average Score', anime.averageScore ? `${anime.averageScore}%` : '???', true) + .addField(`❯ MAL Score`, malScore ? embedURL(malScore, malURL) : '???', true) + .addField(`❯ ${ANILIST_USERNAME}'s Score`, entry && entry.score ? `${entry.score}/10` : '???', true) + .addField('❯ External Links', anime.externalLinks.length + ? anime.externalLinks.map(link => `[${link.site}](${link.url})`).join(', ') + : 'None'); + return msg.embed(embed); } async search(query) { diff --git a/commands/search/define.js b/commands/search/define.js index e1581ef6..8f63945c 100644 --- a/commands/search/define.js +++ b/commands/search/define.js @@ -30,19 +30,15 @@ module.exports = class DefineCommand extends Command { } async run(msg, { word }) { - try { - const { body } = await request - .get(`https://www.dictionaryapi.com/api/v3/references/collegiate/json/${word}`) - .query({ key: WEBSTER_KEY }); - if (!body.length) return msg.say('Could not find any results.'); - const data = body[0]; - if (typeof data === 'string') return msg.say(`Could not find any results. Did you mean **${data}**?`); - return msg.say(stripIndents` - **${data.meta.stems[0]}** (${data.fl}) - ${data.shortdef.map((definition, i) => `(${i + 1}) ${definition}`).join('\n')} - `); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get(`https://www.dictionaryapi.com/api/v3/references/collegiate/json/${word}`) + .query({ key: WEBSTER_KEY }); + if (!body.length) return msg.say('Could not find any results.'); + const data = body[0]; + if (typeof data === 'string') return msg.say(`Could not find any results. Did you mean **${data}**?`); + return msg.say(stripIndents` + **${data.meta.stems[0]}** (${data.fl}) + ${data.shortdef.map((definition, i) => `(${i + 1}) ${definition}`).join('\n')} + `); } }; diff --git a/commands/search/frinkiac.js b/commands/search/frinkiac.js index 59826eec..24342868 100644 --- a/commands/search/frinkiac.js +++ b/commands/search/frinkiac.js @@ -28,32 +28,28 @@ module.exports = class FrinkiacCommand extends Command { } async run(msg, { query }) { - try { - const search = await this.search(query); - if (!search) return msg.say('Could not find any results.'); - const data = await this.fetchCaption(search.Episode, search.Timestamp); - const time = moment.duration(data.Frame.Timestamp).format(); - const caption = data.Subtitles.map(sub => sub.Content).join(' ').split(' '); - let url = `https://frinkiac.com/meme/${data.Frame.Episode}/${data.Frame.Timestamp}.jpg`; - const wrapped = ['']; - let currentLine = 0; - for (const word of caption) { - if (wrapped[currentLine].length + word.length < 26) { - wrapped[currentLine] += ` ${word}`; - } else { - wrapped.push(` ${word}`); - currentLine++; - } + const search = await this.search(query); + if (!search) return msg.say('Could not find any results.'); + const data = await this.fetchCaption(search.Episode, search.Timestamp); + const time = moment.duration(data.Frame.Timestamp).format(); + const caption = data.Subtitles.map(sub => sub.Content).join(' ').split(' '); + let url = `https://frinkiac.com/meme/${data.Frame.Episode}/${data.Frame.Timestamp}.jpg`; + const wrapped = ['']; + let currentLine = 0; + for (const word of caption) { + if (wrapped[currentLine].length + word.length < 26) { + wrapped[currentLine] += ` ${word}`; + } else { + wrapped.push(` ${word}`); + currentLine++; } - url += `?b64lines=${base64(wrapped.join('\n')).replace(/\//g, '_')}`; - const seasonEpisode = `S${data.Episode.Season}E${data.Episode.EpisodeNumber}`; - return msg.say( - `This is from **${seasonEpisode} ("${data.Episode.Title}") @ ${time}**.`, - { files: [url] } - ); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } + url += `?b64lines=${base64(wrapped.join('\n')).replace(/\//g, '_')}`; + const seasonEpisode = `S${data.Episode.Season}E${data.Episode.EpisodeNumber}`; + return msg.say( + `This is from **${seasonEpisode} ("${data.Episode.Title}") @ ${time}**.`, + { files: [url] } + ); } async search(query) { diff --git a/commands/search/google-autofill.js b/commands/search/google-autofill.js index d3919cd0..f618ec41 100644 --- a/commands/search/google-autofill.js +++ b/commands/search/google-autofill.js @@ -26,18 +26,14 @@ module.exports = class GoogleAutofillCommand extends Command { } async run(msg, { query }) { - try { - const { text } = await request - .get('https://suggestqueries.google.com/complete/search') - .query({ - client: 'firefox', - q: query - }); - const data = JSON.parse(text)[1]; - if (!data.length) return msg.say('Could not find any results.'); - return msg.say(data.join('\n')); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { text } = await request + .get('https://suggestqueries.google.com/complete/search') + .query({ + client: 'firefox', + q: query + }); + const data = JSON.parse(text)[1]; + if (!data.length) return msg.say('Could not find any results.'); + return msg.say(data.join('\n')); } }; diff --git a/commands/search/gravatar.js b/commands/search/gravatar.js index 3804ca51..47f3dbd6 100644 --- a/commands/search/gravatar.js +++ b/commands/search/gravatar.js @@ -41,7 +41,7 @@ module.exports = class GravatarCommand extends Command { return msg.say({ files: [{ attachment: body, name: `${emailHash}.jpg` }] }); } catch (err) { if (err.status === 404) return msg.say('Could not find any results.'); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } }; diff --git a/commands/search/http-cat.js b/commands/search/http-cat.js index f5865330..9079b825 100644 --- a/commands/search/http-cat.js +++ b/commands/search/http-cat.js @@ -32,7 +32,7 @@ module.exports = class HttpCatCommand extends Command { return msg.say({ files: [{ attachment: body, name: `${code}.jpg` }] }); } catch (err) { if (err.status === 404) return msg.say('Could not find any results.'); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } }; diff --git a/commands/search/know-your-meme.js b/commands/search/know-your-meme.js index 9d34e837..50c6366a 100644 --- a/commands/search/know-your-meme.js +++ b/commands/search/know-your-meme.js @@ -30,21 +30,17 @@ module.exports = class KnowYourMemeCommand extends Command { } async run(msg, { query }) { - try { - const location = await this.search(query); - if (!location) return msg.say('Could not find any results.'); - const data = await this.fetchMeme(location); - const embed = new MessageEmbed() - .setColor(0x12133F) - .setAuthor('Know Your Meme', 'https://i.imgur.com/WvcH4Z2.png', 'https://knowyourmeme.com/') - .setTitle(data.name) - .setDescription(shorten(data.description || 'No description available.')) - .setURL(data.url) - .setThumbnail(data.thumbnail); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const location = await this.search(query); + if (!location) return msg.say('Could not find any results.'); + const data = await this.fetchMeme(location); + const embed = new MessageEmbed() + .setColor(0x12133F) + .setAuthor('Know Your Meme', 'https://i.imgur.com/WvcH4Z2.png', 'https://knowyourmeme.com/') + .setTitle(data.name) + .setDescription(shorten(data.description || 'No description available.')) + .setURL(data.url) + .setThumbnail(data.thumbnail); + return msg.embed(embed); } async search(query) { diff --git a/commands/search/manga.js b/commands/search/manga.js index 84468bdc..7d17214e 100644 --- a/commands/search/manga.js +++ b/commands/search/manga.js @@ -103,34 +103,30 @@ module.exports = class MangaCommand extends Command { } async run(msg, { query }) { - try { - const id = await this.search(query); - if (!id) return msg.say('Could not find any results.'); - const manga = await this.fetchManga(id); - if (!this.personalList) await this.fetchPersonalList(); - const entry = this.personalList.find(ma => ma.mediaId === id); - const malScore = await this.fetchMALScore(manga.idMal); - const malURL = `https://myanimelist.net/manga/${manga.idMal}`; - const embed = new MessageEmbed() - .setColor(0x02A9FF) - .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') - .setURL(manga.siteUrl) - .setThumbnail(manga.coverImage.large || manga.coverImage.medium || null) - .setTitle(manga.title.english || manga.title.romaji) - .setDescription(manga.description ? cleanAnilistHTML(manga.description) : 'No description.') - .addField('❯ Status', statuses[manga.status], true) - .addField('❯ Chapters / Volumes', `${manga.chapters || '???'}/${manga.volumes || '???'}`, true) - .addField('❯ Year', manga.startDate.year?.toString() || '???', true) - .addField('❯ Average Score', manga.averageScore ? `${manga.averageScore}%` : '???', true) - .addField(`❯ MAL Score`, malScore ? embedURL(malScore, malURL) : '???', true) - .addField(`❯ ${ANILIST_USERNAME}'s Score`, entry && entry.score ? `${entry.score}/10` : '???', true) - .addField('❯ External Links', manga.externalLinks.length - ? manga.externalLinks.map(link => `[${link.site}](${link.url})`).join(', ') - : 'None'); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const id = await this.search(query); + if (!id) return msg.say('Could not find any results.'); + const manga = await this.fetchManga(id); + if (!this.personalList) await this.fetchPersonalList(); + const entry = this.personalList.find(ma => ma.mediaId === id); + const malScore = await this.fetchMALScore(manga.idMal); + const malURL = `https://myanimelist.net/manga/${manga.idMal}`; + const embed = new MessageEmbed() + .setColor(0x02A9FF) + .setAuthor('AniList', 'https://i.imgur.com/iUIRC7v.png', 'https://anilist.co/') + .setURL(manga.siteUrl) + .setThumbnail(manga.coverImage.large || manga.coverImage.medium || null) + .setTitle(manga.title.english || manga.title.romaji) + .setDescription(manga.description ? cleanAnilistHTML(manga.description) : 'No description.') + .addField('❯ Status', statuses[manga.status], true) + .addField('❯ Chapters / Volumes', `${manga.chapters || '???'}/${manga.volumes || '???'}`, true) + .addField('❯ Year', manga.startDate.year?.toString() || '???', true) + .addField('❯ Average Score', manga.averageScore ? `${manga.averageScore}%` : '???', true) + .addField(`❯ MAL Score`, malScore ? embedURL(malScore, malURL) : '???', true) + .addField(`❯ ${ANILIST_USERNAME}'s Score`, entry && entry.score ? `${entry.score}/10` : '???', true) + .addField('❯ External Links', manga.externalLinks.length + ? manga.externalLinks.map(link => `[${link.site}](${link.url})`).join(', ') + : 'None'); + return msg.embed(embed); } async search(query) { diff --git a/commands/search/nasa.js b/commands/search/nasa.js index 0bd112a7..6e9b5b02 100644 --- a/commands/search/nasa.js +++ b/commands/search/nasa.js @@ -29,28 +29,24 @@ module.exports = class NASACommand extends Command { } async run(msg, { query }) { - try { - const { body } = await request - .get('https://images-api.nasa.gov/search') - .query({ - q: query, - media_type: 'image' - }); - const images = body.collection.items; - if (!images.length) return msg.say('Could not find any results.'); - const data = images[Math.floor(Math.random() * images.length)]; - const embed = new MessageEmbed() - .setTitle(shorten(data.data[0].title, 256)) - .setDescription(shorten(this.cleanHTML(data.data[0].description))) - .setColor(0x2E528E) - .setAuthor('NASA', 'https://i.imgur.com/Wh8jY9c.png', 'https://www.nasa.gov/multimedia/imagegallery/index.html') - .setImage(`https://images-assets.nasa.gov/image/${data.data[0].nasa_id}/${data.data[0].nasa_id}~thumb.jpg`) - .setFooter(`Image Credits: ${data.data[0].center || 'Public Domain'}`) - .setTimestamp(new Date(data.data[0].date_created)); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get('https://images-api.nasa.gov/search') + .query({ + q: query, + media_type: 'image' + }); + const images = body.collection.items; + if (!images.length) return msg.say('Could not find any results.'); + const data = images[Math.floor(Math.random() * images.length)]; + const embed = new MessageEmbed() + .setTitle(shorten(data.data[0].title, 256)) + .setDescription(shorten(this.cleanHTML(data.data[0].description))) + .setColor(0x2E528E) + .setAuthor('NASA', 'https://i.imgur.com/Wh8jY9c.png', 'https://www.nasa.gov/multimedia/imagegallery/index.html') + .setImage(`https://images-assets.nasa.gov/image/${data.data[0].nasa_id}/${data.data[0].nasa_id}~thumb.jpg`) + .setFooter(`Image Credits: ${data.data[0].center || 'Public Domain'}`) + .setTimestamp(new Date(data.data[0].date_created)); + return msg.embed(embed); } cleanHTML(text) { diff --git a/commands/search/neopet.js b/commands/search/neopet.js index e5e4e27a..2ec27114 100644 --- a/commands/search/neopet.js +++ b/commands/search/neopet.js @@ -48,7 +48,7 @@ module.exports = class NeopetCommand extends Command { return msg.say({ files: [{ attachment: body, name: `${pet}-${mood}.png` }] }); } catch (err) { if (err.status === 404) return msg.say('Could not find any results.'); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } }; diff --git a/commands/search/neopets-item.js b/commands/search/neopets-item.js index 6e966827..7606ecd0 100644 --- a/commands/search/neopets-item.js +++ b/commands/search/neopets-item.js @@ -34,21 +34,17 @@ module.exports = class NeopetsItemCommand extends Command { } async run(msg, { item }) { - try { - const data = await this.fetchItem(item); - if (!data) return msg.say('Could not find any results.'); - const embed = new MessageEmbed() - .setColor(0xFFCE31) - .setAuthor('Neopets', 'https://i.imgur.com/BP8qxJH.png', 'http://www.neopets.com/') - .setTitle(data.name) - .setDescription(data.details) - .setURL(data.url) - .setThumbnail(data.image) - .addField('❯ Price', data.price ? `${formatNumber(data.price)} ${data.currency}` : 'Not for Sale'); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const data = await this.fetchItem(item); + if (!data) return msg.say('Could not find any results.'); + const embed = new MessageEmbed() + .setColor(0xFFCE31) + .setAuthor('Neopets', 'https://i.imgur.com/BP8qxJH.png', 'http://www.neopets.com/') + .setTitle(data.name) + .setDescription(data.details) + .setURL(data.url) + .setThumbnail(data.image) + .addField('❯ Price', data.price ? `${formatNumber(data.price)} ${data.currency}` : 'Not for Sale'); + return msg.embed(embed); } async fetchItem(query) { diff --git a/commands/search/urban.js b/commands/search/urban.js index 94ddfaed..c9867d17 100644 --- a/commands/search/urban.js +++ b/commands/search/urban.js @@ -30,24 +30,20 @@ module.exports = class UrbanCommand extends Command { } async run(msg, { word }) { - try { - const { body } = await request - .get('http://api.urbandictionary.com/v0/define') - .query({ term: word }); - if (!body.list.length) return msg.say('Could not find any results.'); - const data = body.list[0]; - const embed = new MessageEmbed() - .setColor(0x32A8F0) - .setAuthor('Urban Dictionary', 'https://i.imgur.com/Fo0nRTe.png', 'https://www.urbandictionary.com/') - .setURL(data.permalink) - .setTitle(data.word) - .setDescription(shorten(data.definition.replace(/\[|\]/g, ''))) - .setFooter(`👍 ${formatNumber(data.thumbs_up)} 👎 ${formatNumber(data.thumbs_down)}`) - .setTimestamp(new Date(data.written_on)) - .addField('❯ Example', data.example ? shorten(data.example.replace(/\[|\]/g, ''), 1000) : 'None'); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get('http://api.urbandictionary.com/v0/define') + .query({ term: word }); + if (!body.list.length) return msg.say('Could not find any results.'); + const data = body.list[0]; + const embed = new MessageEmbed() + .setColor(0x32A8F0) + .setAuthor('Urban Dictionary', 'https://i.imgur.com/Fo0nRTe.png', 'https://www.urbandictionary.com/') + .setURL(data.permalink) + .setTitle(data.word) + .setDescription(shorten(data.definition.replace(/\[|\]/g, ''))) + .setFooter(`👍 ${formatNumber(data.thumbs_up)} 👎 ${formatNumber(data.thumbs_down)}`) + .setTimestamp(new Date(data.written_on)) + .addField('❯ Example', data.example ? shorten(data.example.replace(/\[|\]/g, ''), 1000) : 'None'); + return msg.embed(embed); } }; diff --git a/commands/search/wikipedia.js b/commands/search/wikipedia.js index d9934faf..c06ea6f2 100644 --- a/commands/search/wikipedia.js +++ b/commands/search/wikipedia.js @@ -29,30 +29,26 @@ module.exports = class WikipediaCommand extends Command { } async run(msg, { query }) { - try { - const { body } = await request - .get('https://en.wikipedia.org/w/api.php') - .query({ - action: 'query', - prop: 'extracts', - format: 'json', - titles: query, - exintro: '', - explaintext: '', - redirects: '', - formatversion: 2 - }); - const data = body.query.pages[0]; - if (data.missing) return msg.say('Could not find any results.'); - const embed = new MessageEmbed() - .setColor(0xE7E7E7) - .setTitle(data.title) - .setAuthor('Wikipedia', 'https://i.imgur.com/Z7NJBK2.png', 'https://www.wikipedia.org/') - .setURL(`https://en.wikipedia.org/wiki/${encodeURIComponent(query).replaceAll(')', '%29')}`) - .setDescription(shorten(data.extract.replaceAll('\n', '\n\n'))); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); - } + const { body } = await request + .get('https://en.wikipedia.org/w/api.php') + .query({ + action: 'query', + prop: 'extracts', + format: 'json', + titles: query, + exintro: '', + explaintext: '', + redirects: '', + formatversion: 2 + }); + const data = body.query.pages[0]; + if (data.missing) return msg.say('Could not find any results.'); + const embed = new MessageEmbed() + .setColor(0xE7E7E7) + .setTitle(data.title) + .setAuthor('Wikipedia', 'https://i.imgur.com/Z7NJBK2.png', 'https://www.wikipedia.org/') + .setURL(`https://en.wikipedia.org/wiki/${encodeURIComponent(query).replaceAll(')', '%29')}`) + .setDescription(shorten(data.extract.replaceAll('\n', '\n\n'))); + return msg.embed(embed); } }; diff --git a/commands/search/xkcd.js b/commands/search/xkcd.js index 166cb201..159d1d13 100644 --- a/commands/search/xkcd.js +++ b/commands/search/xkcd.js @@ -38,31 +38,19 @@ module.exports = class XKCDCommand extends Command { } async run(msg, { query }) { - try { - const current = await request.get('https://xkcd.com/info.0.json'); - if (query === 'today') { - const embed = new MessageEmbed() - .setTitle(`${current.body.num} - ${current.body.title}`) - .setColor(0x9797FF) - .setURL(`https://xkcd.com/${current.body.num}`) - .setImage(current.body.img) - .setFooter(current.body.alt); - return msg.embed(embed); - } - if (query === 'random') { - const random = Math.floor(Math.random() * current.body.num) + 1; - const { body } = await request.get(`https://xkcd.com/${random}/info.0.json`); - const embed = new MessageEmbed() - .setTitle(`${body.num} - ${body.title}`) - .setColor(0x9797FF) - .setURL(`https://xkcd.com/${body.num}`) - .setImage(body.img) - .setFooter(body.alt); - return msg.embed(embed); - } - const choice = Number.parseInt(query, 10); - if (current.body.num < choice) return msg.say('Could not find any results.'); - const { body } = await request.get(`https://xkcd.com/${choice}/info.0.json`); + const current = await request.get('https://xkcd.com/info.0.json'); + if (query === 'today') { + const embed = new MessageEmbed() + .setTitle(`${current.body.num} - ${current.body.title}`) + .setColor(0x9797FF) + .setURL(`https://xkcd.com/${current.body.num}`) + .setImage(current.body.img) + .setFooter(current.body.alt); + return msg.embed(embed); + } + if (query === 'random') { + const random = Math.floor(Math.random() * current.body.num) + 1; + const { body } = await request.get(`https://xkcd.com/${random}/info.0.json`); const embed = new MessageEmbed() .setTitle(`${body.num} - ${body.title}`) .setColor(0x9797FF) @@ -70,8 +58,16 @@ module.exports = class XKCDCommand extends Command { .setImage(body.img) .setFooter(body.alt); return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); } + const choice = Number.parseInt(query, 10); + if (current.body.num < choice) return msg.say('Could not find any results.'); + const { body } = await request.get(`https://xkcd.com/${choice}/info.0.json`); + const embed = new MessageEmbed() + .setTitle(`${body.num} - ${body.title}`) + .setColor(0x9797FF) + .setURL(`https://xkcd.com/${body.num}`) + .setImage(body.img) + .setFooter(body.alt); + return msg.embed(embed); } }; diff --git a/commands/search/yu-gi-oh.js b/commands/search/yu-gi-oh.js index 43ac0cdc..17213583 100644 --- a/commands/search/yu-gi-oh.js +++ b/commands/search/yu-gi-oh.js @@ -36,39 +36,35 @@ module.exports = class YuGiOhCommand extends Command { } async run(msg, { card }) { - try { - const { body } = await request - .get('https://db.ygoprodeck.com/api/v7/cardinfo.php') - .query({ - fname: card, - la: 'english' - }); - const data = body.data[0]; - const embed = new MessageEmbed() - .setColor(0xBE5F1F) - .setTitle(data.name) - .setURL(`https://db.ygoprodeck.com/card/?search=${data.id}`) - .setDescription(data.type === 'Normal Monster' ? `_${shorten(data.desc)}_` : shorten(data.desc)) - .setAuthor('Yu-Gi-Oh!', 'https://i.imgur.com/AJNBflD.png', 'http://www.yugioh-card.com/') - .setThumbnail(data.card_images[0].image_url) - .setFooter(data.id.toString()) - .addField('❯ Type', data.type, true) - .addField(data.type.includes('Monster') ? '❯ Race' : '❯ Spell Type', data.race, true); - if (data.type.includes('Monster')) { - embed - .addField('❯ Attribute', data.attribute, true) - .addField('❯ Level', data.level?.toString() || 'N/A', true) - .addField('❯ ATK', formatNumber(data.atk).toString(), true) - .addField( - data.type === 'Link Monster' ? '❯ Link Value' : '❯ DEF', - formatNumber(data.type === 'Link Monster' ? data.linkval : data.def).toString(), - true - ); - } - embed.addField('❯ TCGPlayer Price', `$${data.card_prices[0].tcgplayer_price}`); - return msg.embed(embed); - } catch (err) { - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + const { body } = await request + .get('https://db.ygoprodeck.com/api/v7/cardinfo.php') + .query({ + fname: card, + la: 'english' + }); + const data = body.data[0]; + const embed = new MessageEmbed() + .setColor(0xBE5F1F) + .setTitle(data.name) + .setURL(`https://db.ygoprodeck.com/card/?search=${data.id}`) + .setDescription(data.type === 'Normal Monster' ? `_${shorten(data.desc)}_` : shorten(data.desc)) + .setAuthor('Yu-Gi-Oh!', 'https://i.imgur.com/AJNBflD.png', 'http://www.yugioh-card.com/') + .setThumbnail(data.card_images[0].image_url) + .setFooter(data.id.toString()) + .addField('❯ Type', data.type, true) + .addField(data.type.includes('Monster') ? '❯ Race' : '❯ Spell Type', data.race, true); + if (data.type.includes('Monster')) { + embed + .addField('❯ Attribute', data.attribute, true) + .addField('❯ Level', data.level?.toString() || 'N/A', true) + .addField('❯ ATK', formatNumber(data.atk).toString(), true) + .addField( + data.type === 'Link Monster' ? '❯ Link Value' : '❯ DEF', + formatNumber(data.type === 'Link Monster' ? data.linkval : data.def).toString(), + true + ); } + embed.addField('❯ TCGPlayer Price', `$${data.card_prices[0].tcgplayer_price}`); + return msg.embed(embed); } }; diff --git a/commands/voice/dec-talk.js b/commands/voice/dec-talk.js index 542ca727..bf486b8c 100644 --- a/commands/voice/dec-talk.js +++ b/commands/voice/dec-talk.js @@ -60,7 +60,7 @@ module.exports = class DECTalkCommand extends Command { return null; } catch (err) { await reactIfAble(msg, this.client.user, '⚠️'); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } diff --git a/commands/voice/mindfulness.js b/commands/voice/mindfulness.js index 0ec455a9..f554f689 100644 --- a/commands/voice/mindfulness.js +++ b/commands/voice/mindfulness.js @@ -49,7 +49,7 @@ module.exports = class MindfulnessCommand extends Command { return null; } catch (err) { await reactIfAble(msg, this.client.user, '⚠️'); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } diff --git a/commands/voice/tts.js b/commands/voice/tts.js index 57c1e205..73a98d48 100644 --- a/commands/voice/tts.js +++ b/commands/voice/tts.js @@ -69,7 +69,7 @@ module.exports = class TtsCommand extends Command { return null; } catch (err) { await reactIfAble(msg, this.client.user, '⚠️'); - return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); + throw err; } } }; diff --git a/framework/Client.js b/framework/Client.js index 98c6eebc..c53ebd27 100644 --- a/framework/Client.js +++ b/framework/Client.js @@ -1,4 +1,5 @@ const { Client } = require('discord.js'); +const { Collection } = require('@discordjs/collection'); const fs = require('fs'); const path = require('path'); const { stripIndents } = require('common-tags'); @@ -16,6 +17,7 @@ module.exports = class CommandClient extends Client { this.invite = options.invite || null; this.registry = new Registry(this); this.dispatcher = new Dispatcher(this); + this.games = new Collection(); this.blacklist = { user: [], guild: [] }; this._throttlingTimeouts = new Map(); @@ -106,11 +108,21 @@ module.exports = class CommandClient extends Client { } } try { + if (command.game) { + const current = this.games.get(msg.channel.id); + if (current) { + await msg.reply(`Please wait until the current game of \`${current.name}\` is finished.`); + return; + } + this.games.set(msg.channel.id, { name: command.name }); + } const result = await command.run(msg, args); + if (command.game) this.games.delete(msg.channel.id); command.uses++; command.lastRun = new Date(); this.emit('commandRun', command, result, msg, args); } catch (err) { + if (command.game) this.games.delete(msg.channel.id); this.emit('commandError', command, err, msg, args); await msg.reply(stripIndents` An error occurred while running this command: \`${err.message}\`. diff --git a/framework/Command.js b/framework/Command.js index 35881f48..bdc427ac 100644 --- a/framework/Command.js +++ b/framework/Command.js @@ -17,6 +17,7 @@ module.exports = class Command { this.ownerOnly = options.ownerOnly || false; this.nsfw = options.nsfw || false; this.guildOnly = options.guildOnly || false; + this.game = options.game || false; this.guarded = options.guarded || false; this.unknown = options.unknown || false; this.throttling = options.throttling || { usages: 2, duration: 5 }; diff --git a/structures/Client.js b/structures/Client.js index fe3d15e4..783ba784 100644 --- a/structures/Client.js +++ b/structures/Client.js @@ -34,7 +34,6 @@ module.exports = class XiaoClient extends CommandClient { this.redis = Redis ? Redis.db : null; this.timers = new TimerManager(this); this.pokemon = new PokemonStore(); - this.games = new Collection(); this.dispatchers = new Map(); this.cleverbots = new Map(); this.phone = new PhoneManager(this);