Split Single and Multi Player Games

This commit is contained in:
Dragon Fire
2019-12-02 22:38:40 -05:00
parent 53fba29dc0
commit 7dfdc7060c
29 changed files with 42 additions and 38 deletions
+155
View File
@@ -0,0 +1,155 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { shuffle, verify } = require('../../util/Util');
const suits = ['♣', '♥', '♦', '♠'];
const faces = ['Jack', 'Queen', 'King'];
module.exports = class BlackjackCommand extends Command {
constructor(client) {
super(client, {
name: 'blackjack',
aliases: ['twenty-one', '21', 'bj'],
group: 'sp-games',
memberName: 'blackjack',
description: 'Play a game of blackjack.',
args: [
{
key: 'deckCount',
label: 'amount of decks',
prompt: 'How many decks do you want to use?',
type: 'integer',
default: 1,
max: 8,
min: 1
}
]
});
}
async run(msg, { deckCount }) { // eslint-disable-line complexity
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: this.generateDeck(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}
**You (${this.calculate(playerHand)}):**
${playerHand.map(card => card.display).join('\n')}
_Hit?_
`);
const hit = await verify(msg.channel, msg.author);
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;
}
} 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;
}
} 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;
}
}
generateDeck(deckCount) {
const deck = [];
for (let i = 0; i < deckCount; i++) {
for (const suit of suits) {
deck.push({
value: 11,
display: `${suit} Ace`
});
for (let j = 2; j <= 10; j++) {
deck.push({
value: j,
display: `${suit} ${j}`
});
}
for (const face of faces) {
deck.push({
value: 10,
display: `${suit} ${face}`
});
}
}
}
return shuffle(deck);
}
draw(channel, hand) {
const deck = this.client.games.get(channel.id).data;
const card = deck[0];
deck.shift();
hand.push(card);
return card;
}
calculate(hand) {
return hand.sort((a, b) => a.value - b.value).reduce((a, b) => {
let { value } = b;
if (value === 11 && a + value > 21) value = 1;
return a + value;
}, 0);
}
};
+85
View File
@@ -0,0 +1,85 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { verify } = require('../../util/Util');
const script = require('../../assets/json/box-choosing');
module.exports = class BoxChoosingCommand extends Command {
constructor(client) {
super(client, {
name: 'box-choosing',
aliases: ['box-choose'],
group: 'sp-games',
memberName: 'box-choosing',
description: 'Do you believe that there are choices in life? Taken from Higurashi Chapter 4.',
credit: [
{
name: '07th Expansion',
url: 'http://07th-expansion.net/'
},
{
name: 'MangaGamer.com',
url: 'https://www.mangagamer.com/'
},
{
name: 'Higurashi When They Cry Hou - Ch.4 Himatsubushi',
url: 'https://store.steampowered.com/app/526490/'
}
]
});
this.blue = new Set();
this.red = new Set();
}
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';
while (true) { // eslint-disable-line no-constant-condition
const line = script[path][i];
if (line.end) {
this.client.games.delete(msg.channel.id);
return msg.say(line.text);
} else {
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) 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;
} else {
const verification = await verify(msg.channel, msg.author, 120000);
if (!verification) break;
i++;
}
}
this.client.games.delete(msg.channel.id);
return msg.say('See you soon!');
} catch (err) {
this.client.games.delete(msg.channel.id);
throw err;
}
}
};
+58
View File
@@ -0,0 +1,58 @@
const Command = require('../../structures/Command');
const { createCanvas, registerFont } = require('canvas');
const path = require('path');
const pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ23456789'.split('');
registerFont(path.join(__dirname, '..', '..', 'assets', 'fonts', 'Captcha.ttf'), { family: 'Captcha' });
module.exports = class CaptchaCommand extends Command {
constructor(client) {
super(client, {
name: 'captcha',
aliases: ['captcha-quiz'],
group: 'sp-games',
memberName: 'captcha',
description: 'Try to guess what the captcha says.',
throttling: {
usages: 1,
duration: 10
},
clientPermissions: ['ATTACH_FILES'],
credit: [
{
name: 'Moms Typewriter Font',
url: 'https://www.fontsquirrel.com/fonts/MomsTypewriter'
}
]
});
}
async run(msg) {
const canvas = createCanvas(125, 32);
const ctx = canvas.getContext('2d');
const text = this.randomText(5);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.strokeStyle = '#0088cc';
ctx.font = '26px Captcha';
ctx.rotate(-0.05);
ctx.strokeText(text, 15, 26);
await msg.reply(
'**You have 15 seconds, what does the captcha say?**',
{ files: [{ attachment: canvas.toBuffer(), name: 'captcha-quiz.png' }] }
);
const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, {
max: 1,
time: 15000
});
if (!msgs.size) return msg.reply(`Sorry, time is up! It was ${text}.`);
if (msgs.first().content !== text) return msg.reply(`Nope, sorry, it's ${text}.`);
return msg.reply('Nice job! 10/10! You deserve some cake!');
}
randomText(len) {
const result = [];
for (let i = 0; i < len; i++) result.push(pool[Math.floor(Math.random() * pool.length)]);
return result.join('');
}
};
+27
View File
@@ -0,0 +1,27 @@
const Command = require('../../structures/Command');
module.exports = class ChanceCommand extends Command {
constructor(client) {
super(client, {
name: 'chance',
aliases: ['1-in', 'one-in'],
group: 'sp-games',
memberName: 'chance',
description: 'Attempt to win with a 1 in 1000 (or your choice) chance of winning.',
args: [
{
key: 'chance',
prompt: 'What is the chance of winning? 1 in what?',
type: 'string',
default: 1000
}
]
});
}
run(msg, { chance }) {
const loss = Math.floor(Math.random() * chance);
if (!loss) return msg.reply('Nice job! 10/10! You deserve some cake!');
return msg.reply('Nope, sorry, you lost.');
}
};
+53
View File
@@ -0,0 +1,53 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { verify } = require('../../util/Util');
const doors = [1, 2, 3];
module.exports = class DoorsCommand extends Command {
constructor(client) {
super(client, {
name: 'doors',
aliases: ['door', 'door-opening', 'open-door', 'monty-hall'],
group: 'sp-games',
memberName: 'doors',
description: 'Open the right door, and you win the money! Make the wrong choice, and you get the fire!',
args: [
{
key: 'door',
prompt: 'Which door number do you want to pick? A number from 1-3.',
type: 'integer',
min: 1,
max: 3
}
]
});
}
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;
}
}
emoji(door, noWin, win, chosen) {
return door === win && chosen === win ? '💰' : door === noWin ? '🔥' : door === chosen ? '🔥' : '🚪';
}
};
+27
View File
@@ -0,0 +1,27 @@
const Command = require('../../structures/Command');
const { randomRange } = require('../../util/Util');
const fishes = require('../../assets/json/fishy');
module.exports = class FishyCommand extends Command {
constructor(client) {
super(client, {
name: 'fishy',
aliases: ['fishing'],
group: 'sp-games',
memberName: 'fishy',
description: 'Go fishing.'
});
}
run(msg) {
const fishID = Math.floor(Math.random() * 10) + 1;
let rarity;
if (fishID < 5) rarity = 'junk';
else if (fishID < 8) rarity = 'common';
else if (fishID < 10) rarity = 'uncommon';
else rarity = 'rare';
const fish = fishes[rarity];
const worth = randomRange(fish.min, fish.max);
return msg.reply(`You caught a ${fish.symbol}. I bet it'd sell for around $${worth}.`);
}
};
+80
View File
@@ -0,0 +1,80 @@
const Command = require('../../structures/Command');
const request = require('node-superfetch');
const { MessageEmbed } = require('discord.js');
const questions = require('../../assets/json/google-feud');
module.exports = class GoogleFeudCommand extends Command {
constructor(client) {
super(client, {
name: 'google-feud',
group: 'sp-games',
memberName: 'google-feud',
description: 'Attempt to determine the top suggestions for a Google search.',
credit: [
{
name: 'Google Feud',
url: 'http://www.googlefeud.com/'
}
],
args: [
{
key: 'question',
prompt: 'What question do you want to use for the game?',
type: 'string',
default: () => questions[Math.floor(Math.random() * questions.length)]
}
]
});
}
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 suggestions = await this.fetchSuggestions(question);
if (!suggestions) return msg.say('Could not find any results.');
const display = new Array(suggestions.length).fill('???');
let tries = 3;
while (display.includes('???') && tries) {
const embed = new MessageEmbed()
.setColor(0x005AF0)
.setTitle(`${question}...?`)
.setDescription('Type the choice you think is a suggestion _without_ the question.')
.setFooter(`${tries} ${tries === 1 ? 'try' : 'tries'} remaining!`);
for (let i = 0; i < suggestions.length; i++) embed.addField(` ${10000 - (i * 1000)}`, display[i], true);
await msg.embed(embed);
const msgs = await msg.channel.awaitMessages(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)) display[suggestions.indexOf(choice)] = choice;
else --tries;
}
this.client.games.delete(msg.channel.id);
if (!display.includes('???')) return msg.say('You win! Nice job, master of Google!');
return msg.say('Better luck next time!');
} catch (err) {
this.client.games.delete(msg.channel.id);
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
}
}
async fetchSuggestions(question) {
const { text } = await request
.get('https://suggestqueries.google.com/complete/search')
.query({
client: 'firefox',
q: question
});
const suggestions = JSON.parse(text)[1]
.filter(suggestion => suggestion.toLowerCase() !== question.toLowerCase());
if (!suggestions.length) return null;
return suggestions.map(suggestion => suggestion.toLowerCase().replace(question.toLowerCase(), '').trim());
}
};
+85
View File
@@ -0,0 +1,85 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const words = require('../../assets/json/word-list');
module.exports = class HangmanCommand extends Command {
constructor(client) {
super(client, {
name: 'hangman',
group: 'sp-games',
memberName: 'hangman',
description: 'Prevent a man from being hanged by guessing a word as fast as you can.',
credit: [
{
name: 'Moby Word Lists by Grady Ward',
url: 'http://www.gutenberg.org/ebooks/3201'
}
]
});
}
async run(msg) { // eslint-disable-line complexity
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?
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++;
}
}
this.client.games.delete(msg.channel.id);
if (word.length === confirmation.length || guessed) return msg.say(`You won, it was ${word}!`);
return msg.say(`Too bad... It was ${word}...`);
} catch (err) {
this.client.games.delete(msg.channel.id);
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
}
}
};
+123
View File
@@ -0,0 +1,123 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { shuffle, verify } = require('../../util/Util');
const events = require('../../assets/json/hunger-games');
module.exports = class HungerGamesCommand extends Command {
constructor(client) {
super(client, {
name: 'hunger-games',
aliases: ['hunger-games-simulator', 'hunger-games-sim'],
group: 'sp-games',
memberName: 'hunger-games',
description: 'Simulate a Hunger Games match with up to 24 tributes.',
credit: [
{
name: 'BrantSteele Hunger Games Simulator',
url: 'http://brantsteele.net/hungergames/reaping.php'
}
],
args: [
{
key: 'tributes',
prompt: 'Who should compete in the games? Up to 24 tributes can participate.',
type: 'string',
infinite: true,
max: 20
}
]
});
}
async run(msg, { tributes }) {
if (tributes.length < 2) return msg.say(`...${tributes[0]} wins, as they were the only tribute.`);
if (tributes.length > 24) return msg.reply('Please do not enter more than 24 tributes.');
if (new Set(tributes).size !== 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 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, 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')}
`;
}
text += `\n\n_Proceed?_`;
await msg.say(text);
const verification = await verify(msg.channel, msg.author, 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(`And the winner is... ${remainingArr[0]}!`);
} catch (err) {
this.client.games.delete(msg.channel.id);
throw err;
}
}
parseEvent(event, tributes) {
return event
.replace(/\(Player1\)/gi, `**${tributes[0]}**`)
.replace(/\(Player2\)/gi, `**${tributes[1]}**`)
.replace(/\(Player3\)/gi, `**${tributes[2]}**`)
.replace(/\(Player4\)/gi, `**${tributes[3]}**`)
.replace(/\(Player5\)/gi, `**${tributes[4]}**`)
.replace(/\(Player6\)/gi, `**${tributes[5]}**`);
}
makeEvents(tributes, eventsArr, deaths, results) {
const turn = new Set(tributes);
for (const tribute of tributes) {
if (!turn.has(tribute)) continue;
const valid = eventsArr.filter(event => event.tributes <= turn.size && event.deaths < turn.size);
const event = valid[Math.floor(Math.random() * valid.length)];
turn.delete(tribute);
if (event.tributes === 1) {
if (event.deaths.length === 1) {
deaths.push(tribute);
tributes.delete(tribute);
}
results.push(this.parseEvent(event.text, [tribute]));
} else {
const current = [tribute];
if (event.deaths.includes(1)) {
deaths.push(tribute);
tributes.delete(tribute);
}
for (let i = 2; i <= event.tributes; i++) {
const turnArr = Array.from(turn);
const tribu = turnArr[Math.floor(Math.random() * turnArr.length)];
if (event.deaths.includes(i)) {
deaths.push(tribu);
tributes.delete(tribu);
}
current.push(tribu);
turn.delete(tribu);
}
results.push(this.parseEvent(event.text, current));
}
}
}
};
+34
View File
@@ -0,0 +1,34 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const prizes = ['$0', '$2', '$4', '$10', '$500', '$1,000,000', 'the Jackpot'];
module.exports = class LotteryCommand extends Command {
constructor(client) {
super(client, {
name: 'lottery',
aliases: ['lotto'],
group: 'sp-games',
memberName: 'lottery',
description: 'Attempt to win the lottery with 6 numbers.',
args: [
{
key: 'choices',
prompt: 'What numbers do you choose? Only the first six will be counted.',
type: 'integer',
infinite: true,
max: 70,
min: 1
}
]
});
}
run(msg, { choices }) {
const lotto = Array.from({ length: 6 }, () => Math.floor(Math.random() * 70) + 1);
const similarities = lotto.filter((num, i) => choices[i] === num).length;
return msg.reply(stripIndents`
${lotto.join(', ')}
You matched **${similarities}** numbers, which gives you **${prizes[similarities]}**! Congrats!
`);
}
};
+56
View File
@@ -0,0 +1,56 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { list } = require('../../util/Util');
const difficulties = ['easy', 'medium', 'hard', 'extreme', 'impossible'];
const operations = ['+', '-', '*'];
const maxValues = {
easy: 10,
medium: 100,
hard: 500,
extreme: 1000,
impossible: Number.MAX_SAFE_INTEGER
};
module.exports = class MathQuizCommand extends Command {
constructor(client) {
super(client, {
name: 'math-quiz',
group: 'sp-games',
memberName: 'math-quiz',
description: 'See how fast you can answer a math problem in a given time limit.',
details: `**Difficulties:** ${difficulties.join(', ')}`,
args: [
{
key: 'difficulty',
prompt: `What should the difficulty of the game be? Either ${list(difficulties, 'or')}.`,
type: 'string',
oneOf: difficulties,
parse: difficulty => difficulty.toLowerCase()
}
]
});
}
async run(msg, { difficulty }) {
const value1 = Math.floor(Math.random() * maxValues[difficulty]) + 1;
const value2 = Math.floor(Math.random() * maxValues[difficulty]) + 1;
const operation = operations[Math.floor(Math.random() * operations.length)];
let answer;
switch (operation) {
case '+': answer = value1 + value2; break;
case '-': answer = value1 - value2; break;
case '*': answer = value1 * value2; break;
}
await msg.reply(stripIndents`
**You have 10 seconds to answer this question.**
${value1} ${operation} ${value2}
`);
const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, {
max: 1,
time: 10000
});
if (!msgs.size) return msg.reply(`Sorry, time is up! It was ${answer}.`);
if (msgs.first().content !== answer.toString()) return msg.reply(`Nope, sorry, it's ${answer}.`);
return msg.reply('Nice job! 10/10! You deserve some cake!');
}
};
+81
View File
@@ -0,0 +1,81 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const request = require('node-superfetch');
const { shuffle, list } = require('../../util/Util');
const types = ['multiple', 'boolean'];
const difficulties = ['easy', 'medium', 'hard'];
const choices = ['A', 'B', 'C', 'D'];
module.exports = class QuizCommand extends Command {
constructor(client) {
super(client, {
name: 'quiz',
aliases: ['trivia'],
group: 'sp-games',
memberName: 'quiz',
description: 'Answer a quiz question.',
details: stripIndents`
**Types:** ${types.join(', ')}
**Difficulties:** ${difficulties.join(', ')}
`,
credit: [
{
name: 'Open Trivia DB',
url: 'https://opentdb.com/'
}
],
args: [
{
key: 'type',
prompt: `Which type of question would you like to have? Either ${list(types, 'or')}.`,
type: 'string',
default: 'multiple',
oneOf: types,
parse: type => type.toLowerCase()
},
{
key: 'difficulty',
prompt: `What should the difficulty of the game be? Either ${list(difficulties, 'or')}.`,
type: 'string',
default: '',
oneOf: difficulties,
parse: difficulty => difficulty.toLowerCase()
}
]
});
}
async run(msg, { type, difficulty }) {
try {
const { body } = await request
.get('https://opentdb.com/api.php')
.query({
amount: 1,
type,
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 to answer this question.**
${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!');
} catch (err) {
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
}
}
};
+42
View File
@@ -0,0 +1,42 @@
const Command = require('../../structures/Command');
const choices = ['rock', 'paper', 'scissors'];
module.exports = class RockPaperScissorsCommand extends Command {
constructor(client) {
super(client, {
name: 'rock-paper-scissors',
aliases: ['rps'],
group: 'sp-games',
memberName: 'rock-paper-scissors',
description: 'Play Rock-Paper-Scissors.',
args: [
{
key: 'choice',
prompt: 'Rock, Paper, or Scissors?',
type: 'string',
parse: choice => choice.toLowerCase()
}
]
});
}
run(msg, { choice }) {
const response = choices[Math.floor(Math.random() * choices.length)];
if (choice === 'rock') {
if (response === 'rock') return msg.reply('Rock! Aw... A tie...');
if (response === 'paper') return msg.reply('Paper! Yes! I win!');
if (response === 'scissors') return msg.reply('Scissors! Aw... I lose...');
}
if (choice === 'paper') {
if (response === 'rock') return msg.reply('Rock! Aw... I lose...');
if (response === 'paper') return msg.reply('Paper! Aw... A tie...');
if (response === 'scissors') return msg.reply('Scissors! Yes! I win!');
}
if (choice === 'scissors') {
if (response === 'rock') return msg.reply('Rock! Yes! I win!');
if (response === 'paper') return msg.reply('Paper! Aw... I lose...');
if (response === 'scissors') return msg.reply('Scissors! Aw... A tie...');
}
return msg.reply('I win by default, you little cheater.');
}
};
+65
View File
@@ -0,0 +1,65 @@
const Command = require('../../structures/Command');
const { oneLine } = require('common-tags');
const red = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36];
const black = [2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35];
const numbers = [0].concat(red, black);
const dozens = ['1-12', '13-24', '25-36'];
const halves = ['1-18', '19-36'];
const columns = ['1st', '2nd', '3rd'];
const parity = ['even', 'odd'];
const colors = ['red', 'black'];
module.exports = class RouletteCommand extends Command {
constructor(client) {
super(client, {
name: 'roulette',
group: 'sp-games',
memberName: 'roulette',
description: 'Play a game of roulette.',
args: [
{
key: 'space',
prompt: 'What space do you want to bet on?',
type: 'string',
validate: space => {
if (numbers.includes(Number.parseInt(space, 10))) return true;
if (dozens.includes(space)) return true;
if (halves.includes(space)) return true;
if (columns.includes(space.toLowerCase())) return true;
if (parity.includes(space.toLowerCase())) return true;
if (colors.includes(space.toLowerCase())) return true;
return oneLine`
Invalid space, please enter either a specific number from 0-36, a range of dozens (e.g. 1-12), a range of
halves (e.g. 1-18), a column (e.g. 1st), a color (e.g. black), or a parity (e.g. even).
`;
},
parse: space => space.toLowerCase()
}
]
});
}
run(msg, { space }) {
const number = Math.floor(Math.random() * 37);
const color = number ? red.includes(number) ? 'RED' : 'BLACK' : null;
const win = this.verifyWin(space, number);
return msg.reply(`The result is **${number}${color ? ` ${color}` : ''}**. ${win ? 'You win!' : 'You lose...'}`);
}
verifyWin(choice, result) {
if (dozens.includes(choice) || halves.includes(choice)) {
const range = choice.split('-');
return result >= range[0] && range[1] >= result;
}
if (colors.includes(choice)) {
if (choice === 'black') return black.includes(result);
if (choice === 'red') return red.includes(result);
}
if (parity.includes(choice)) return parity[result % 2] === choice;
if (columns.includes(choice)) return columns[(result - 1) % 3] === choice;
const number = Number.parseInt(choice, 10);
if (numbers.includes(number)) return result === number;
if (!result) return false;
return false;
}
};
+30
View File
@@ -0,0 +1,30 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const slots = ['🍇', '🍊', '🍐', '🍒', '🍋'];
module.exports = class SlotsCommand extends Command {
constructor(client) {
super(client, {
name: 'slots',
group: 'sp-games',
memberName: 'slots',
description: 'Play a game of slots.'
});
}
run(msg) {
const slotOne = slots[Math.floor(Math.random() * slots.length)];
const slotTwo = slots[Math.floor(Math.random() * slots.length)];
const slotThree = slots[Math.floor(Math.random() * slots.length)];
if (slotOne === slotTwo && slotOne === slotThree) {
return msg.reply(stripIndents`
${slotOne}|${slotTwo}|${slotThree}
Wow! You won! Great job... er... luck!
`);
}
return msg.reply(stripIndents`
${slotOne}|${slotTwo}|${slotThree}
Aww... You lost... Guess it's just bad luck, huh?
`);
}
};
+88
View File
@@ -0,0 +1,88 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { shuffle } = require('../../util/Util');
const { questions, houses, descriptions } = require('../../assets/json/sorting-hat');
const choices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'];
module.exports = class SortingHatCommand extends Command {
constructor(client) {
super(client, {
name: 'sorting-hat',
aliases: ['sorting-hat-quiz', 'hogwarts', 'hogwarts-house'],
group: 'sp-games',
memberName: 'sorting-hat',
description: 'Take a quiz to determine your Hogwarts house.',
credit: [
{
name: 'Pottermore',
url: 'https://my.pottermore.com/sorting'
},
{
name: 'Pottermore Sorting Hat Quiz analysis by u/N1ffler',
url: 'https://www.reddit.com/r/Pottermore/comments/44os14/pottermore_sorting_hat_quiz_analysis/'
}
]
});
}
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 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')}
`);
} catch (err) {
this.client.games.delete(msg.channel.id);
throw err;
}
}
};
+49
View File
@@ -0,0 +1,49 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { list } = require('../../util/Util');
const sentences = require('../../assets/json/typing-test');
const difficulties = ['easy', 'medium', 'hard', 'extreme', 'impossible'];
const times = {
easy: 25000,
medium: 20000,
hard: 15000,
extreme: 10000,
impossible: 5000
};
module.exports = class TypingTestCommand extends Command {
constructor(client) {
super(client, {
name: 'typing-test',
group: 'sp-games',
memberName: 'typing-test',
description: 'See how fast you can type a sentence in a given time limit.',
details: `**Difficulties:** ${difficulties.join(', ')}`,
args: [
{
key: 'difficulty',
prompt: `What should the difficulty of the game be? Either ${list(difficulties, 'or')}.`,
type: 'string',
oneOf: difficulties,
parse: difficulty => difficulty.toLowerCase()
}
]
});
}
async run(msg, { difficulty }) {
const sentence = sentences[Math.floor(Math.random() * sentences.length)];
const time = times[difficulty];
await msg.reply(stripIndents`
**You have ${time / 1000} seconds to type this sentence.**
${sentence}
`);
const now = Date.now();
const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, {
max: 1,
time
});
if (!msgs.size || msgs.first().content !== sentence) return msg.reply('Sorry! You lose!');
return msg.reply(`Nice job! 10/10! You deserve some cake! (Took ${(Date.now() - now) / 1000} seconds)`);
}
};
+74
View File
@@ -0,0 +1,74 @@
const Command = require('../../structures/Command');
const { createCanvas, loadImage } = require('canvas');
const request = require('node-superfetch');
const { silhouette } = require('../../util/Canvas');
module.exports = class WhosThatPokemonCommand extends Command {
constructor(client) {
super(client, {
name: 'whos-that-pokemon',
aliases: ['who-pokemon', 'whos-that-pokémon', 'who-pokémon'],
group: 'sp-games',
memberName: 'whos-that-pokemon',
description: 'Guess who that Pokémon is.',
throttling: {
usages: 1,
duration: 10
},
clientPermissions: ['ATTACH_FILES'],
credit: [
{
name: 'Pokémon',
url: 'https://www.pokemon.com/us/'
},
{
name: 'PokéAPI',
url: 'https://pokeapi.co/'
},
{
name: 'Serebii.net',
url: 'https://www.serebii.net/index2.shtml'
}
],
args: [
{
key: 'hide',
prompt: 'Do you want to silhouette the Pokémon\'s image?',
type: 'boolean',
default: false
}
]
});
}
async run(msg, { hide }) {
const pokemon = Math.floor(Math.random() * 802) + 1;
try {
const data = await this.client.pokemon.fetch(pokemon.toString());
const names = data.names.map(name => name.name.toLowerCase());
const attachment = await this.fetchImage(data, hide);
await msg.reply('**You have 15 seconds, who\'s that Pokémon?**', { files: [attachment] });
const msgs = await msg.channel.awaitMessages(res => res.author.id === msg.author.id, {
max: 1,
time: 15000
});
if (!msgs.size) return msg.reply(`Sorry, time is up! It was ${data.name}.`);
if (!names.includes(msgs.first().content.toLowerCase())) return msg.reply(`Nope, sorry, it's ${data.name}.`);
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!`);
}
}
async fetchImage(pokemon, hide = false) {
const name = `${pokemon.id}.png`;
const image = await request.get(pokemon.spriteImageURL);
if (!hide) return { attachment: image.body, name };
const base = await loadImage(image.body);
const canvas = createCanvas(base.width, base.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(base, 0, 0);
silhouette(ctx, 0, 0, base.width, base.height);
return { attachment: canvas.toBuffer(), name };
}
};