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
+80
View File
@@ -0,0 +1,80 @@
const Command = require('../../structures/Command');
const { randomRange, verify } = require('../../util/Util');
module.exports = class BalloonPopCommand extends Command {
constructor(client) {
super(client, {
name: 'balloon-pop',
group: 'mp-games',
memberName: 'balloon-pop',
description: 'Don\'t let yourself be the last one to pump the balloon before it pops!',
credit: [
{
name: 'PAC-MAN Party',
url: 'http://pacman.com/en/pac-man-games/pac-man-party'
}
],
args: [
{
key: 'opponent',
prompt: 'What user would you like to play against?',
type: 'user'
}
]
});
}
async run(msg, { opponent }) {
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.');
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 userTurn = false;
let winner = null;
let remains = 500;
let turns = 0;
while (!winner) {
const user = userTurn ? msg.author : opponent;
let pump;
++turns;
if (turns === 1) {
await msg.say(`${user} pumps the balloon!`);
pump = true;
} else {
await msg.say(`${user}, do you pump the balloon again?`);
pump = await verify(msg.channel, user);
}
if (pump) {
remains -= randomRange(25, 75);
const popped = Math.floor(Math.random() * remains);
if (popped <= 0) {
await msg.say('The balloon pops!');
winner = userTurn ? opponent : msg.author;
break;
}
if (turns >= 3) {
await msg.say(`${user} steps back!`);
turns = 0;
userTurn = !userTurn;
}
} else {
turns = 0;
userTurn = !userTurn;
}
}
this.client.games.delete(msg.channel.id);
return msg.say(`And the winner is... ${winner}! Great job!`);
} catch (err) {
this.client.games.delete(msg.channel.id);
throw err;
}
}
};
+91
View File
@@ -0,0 +1,91 @@
const Command = require('../../structures/Command');
const Battle = require('../../structures/battle/Battle');
const { randomRange, verify } = require('../../util/Util');
module.exports = class BattleCommand extends Command {
constructor(client) {
super(client, {
name: 'battle',
aliases: ['fight', 'death-battle'],
group: 'mp-games',
memberName: 'battle',
description: 'Engage in a turn-based battle against another user or the AI.',
args: [
{
key: 'opponent',
prompt: 'What user would you like to battle?',
type: 'user',
default: () => this.client.user
}
]
});
}
async run(msg, { opponent }) {
if (opponent.id === msg.author.id) return msg.reply('You may not battle yourself.');
const current = this.client.games.get(msg.channel.id);
if (current) return msg.reply(`Please wait until the current game of \`${current.name}\` is finished.`);
this.client.games.set(msg.channel.id, { name: this.name, 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() * 3);
if (miss) {
await msg.say(`${battle.attacker}'s special attack missed!`);
} else {
const damage = randomRange(battle.defender.guard ? 50 : 100, battle.defender.guard ? 100 : 150);
await msg.say(`${battle.attacker} deals **${damage}** damage!`);
battle.defender.dealDamage(damage);
}
battle.attacker.useMP(50);
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 **150** damage!`);
battle.defender.dealDamage(150);
battle.attacker.useMP(100);
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();
} else {
await msg.say('I do not understand what you want to do.');
}
}
const { winner } = battle;
this.client.games.delete(msg.channel.id);
return msg.say(`The match is over! Congrats, ${winner}!`);
} catch (err) {
this.client.games.delete(msg.channel.id);
throw err;
}
}
};
@@ -0,0 +1,72 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { verify } = require('../../util/Util');
const emojis = ['⬆', '↗', '➡', '↘', '⬇', '↙', '⬅', '↖'];
module.exports = class EmojiEmojiRevolutionCommand extends Command {
constructor(client) {
super(client, {
name: 'emoji-emoji-revolution',
aliases: ['eer'],
group: 'mp-games',
memberName: 'emoji-emoji-revolution',
description: 'Can you type arrow emoji faster than anyone else has ever typed them before?',
guildOnly: true,
args: [
{
key: 'opponent',
prompt: 'What user would you like to play against?',
type: 'user'
}
]
});
}
async run(msg, { opponent }) {
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.');
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;
while (turn < 10) {
++turn;
const emoji = emojis[Math.floor(Math.random() * emojis.length)];
await msg.say(emoji);
const filter = res => [msg.author.id, opponent.id].includes(res.author.id) && res.content === emoji;
const win = await msg.channel.awaitMessages(filter, {
max: 1,
time: 30000
});
if (!win.size) {
await msg.say('Hmm... No one even tried that round.');
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}
`);
}
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;
}
}
};
+54
View File
@@ -0,0 +1,54 @@
const Command = require('../../structures/Command');
const { delay, randomRange, verify } = require('../../util/Util');
const words = ['fire', 'draw', 'shoot', 'bang', 'pull', 'boom'];
module.exports = class GunfightCommand extends Command {
constructor(client) {
super(client, {
name: 'gunfight',
aliases: ['western-gunfight'],
group: 'mp-games',
memberName: 'gunfight',
description: 'Engage in a western gunfight against another user. High noon.',
guildOnly: true,
args: [
{
key: 'opponent',
prompt: 'What user would you like to gunfight?',
type: 'user'
}
]
});
}
async run(msg, { opponent }) {
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.');
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 winner = await msg.channel.awaitMessages(filter, {
max: 1,
time: 30000
});
this.client.games.delete(msg.channel.id);
if (!winner.size) return msg.say('Oh... No one won.');
return msg.say(`The winner is ${winner.first().author}!`);
} catch (err) {
this.client.games.delete(msg.channel.id);
throw err;
}
}
};
+123
View File
@@ -0,0 +1,123 @@
const Command = require('../../structures/Command');
const { stripIndents, oneLine } = require('common-tags');
const request = require('node-superfetch');
const { shuffle, verify } = require('../../util/Util');
const choices = ['A', 'B', 'C', 'D'];
module.exports = class QuizDuelCommand extends Command {
constructor(client) {
super(client, {
name: 'quiz-duel',
aliases: ['trivia-duel'],
group: 'mp-games',
memberName: 'quiz-duel',
description: 'Answer a series of quiz questions against an opponent.',
credit: [
{
name: 'Open Trivia DB',
url: 'https://opentdb.com/'
}
],
args: [
{
key: 'opponent',
prompt: 'What user would you like to play against?',
type: 'user'
},
{
key: 'maxPts',
label: 'maximum amount of points',
prompt: 'What amount of points should determine the winner?',
type: 'integer',
min: 1,
max: 10
}
]
});
}
async run(msg, { opponent, maxPts }) {
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.');
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 winner = null;
let userPoints = 0;
let oppoPoints = 0;
while (!winner) {
const question = await this.fetchQuestion();
await msg.say(stripIndents`
**You have 15 seconds to answer this question.**
${question.question}
${question.answers.map((answer, i) => `**${choices[i]}.** ${answer}`).join('\n')}
`);
const answered = [];
const filter = res => {
const choice = res.content.toUpperCase();
if (!choices.includes(choice) || answered.includes(res.author.id)) return false;
if (![msg.author.id, opponent.id].includes(res.author.id)) return false;
answered.push(res.author.id);
if (question.answers[choices.indexOf(res.content.toUpperCase())] !== question.correct) {
msg.say(`${res.author}, that's incorrect!`).catch(() => null);
return false;
}
return true;
};
const msgs = await msg.channel.awaitMessages(filter, {
max: 1,
time: 15000
});
if (!msgs.size) {
await msg.say(`Sorry, time is up! It was ${question.correct}.`);
continue;
}
const result = msgs.first();
const userWin = result.author.id === msg.author.id;
if (userWin) ++userPoints;
else ++oppoPoints;
if (userPoints >= maxPts) winner = msg.author;
else if (oppoPoints >= maxPts) winner = opponent;
const score = oneLine`
${userWin ? '**' : ''}${userPoints}${userWin ? '**' : ''} -
${userWin ? '' : '**'}${oppoPoints}${userWin ? '' : '**'}
`;
await msg.say(`Nice one, ${result.author}! The score is now ${score}!`);
}
this.client.games.delete(msg.channel.id);
if (!winner) return msg.say('Aww, no one won...');
return msg.say(`Congrats, ${winner}, you won!`);
} catch (err) {
this.client.games.delete(msg.channel.id);
return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`);
}
}
async fetchQuestion() {
const { body } = await request
.get('https://opentdb.com/api.php')
.query({
amount: 1,
type: 'multiple',
encode: 'url3986'
});
if (!body.results) return this.fetchQuestion();
const question = body.results[0];
const answers = question.incorrect_answers.map(answer => decodeURIComponent(answer.toLowerCase()));
const correct = decodeURIComponent(question.correct_answer.toLowerCase());
answers.push(correct);
const shuffled = shuffle(answers);
return {
question: decodeURIComponent(question.question),
answers: shuffled,
correct
};
}
};
+66
View File
@@ -0,0 +1,66 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { shuffle, verify } = require('../../util/Util');
module.exports = class RussianRouletteCommand extends Command {
constructor(client) {
super(client, {
name: 'russian-roulette',
aliases: ['r-roulette', 'russia-gun'],
group: 'mp-games',
memberName: 'russian-roulette',
description: 'Who will pull the trigger and die first?',
args: [
{
key: 'opponent',
prompt: 'What user would you like to gunfight?',
type: 'user',
default: () => this.client.user
}
]
});
}
async run(msg, { opponent }) {
if (opponent.id === msg.author.id) return msg.reply('You may not challenge yourself.');
const current = this.client.games.get(msg.channel.id);
if (current) return msg.reply(`Please wait until the current game of \`${current.name}\` is finished.`);
this.client.games.set(msg.channel.id, { name: this.name });
try {
if (!opponent.bot) {
await msg.say(`${opponent}, do you accept this challenge?`);
const verification = await verify(msg.channel, opponent);
if (!verification) {
this.client.games.delete(msg.channel.id);
return msg.say('Looks like they declined...');
}
}
let userTurn = true;
const gun = shuffle([true, false, false, false, false, false, false, false]);
let round = 0;
let winner = null;
while (!winner) {
const player = userTurn ? msg.author : opponent;
const notPlayer = userTurn ? opponent : msg.author;
if (gun[round]) {
await msg.say(`**${player.tag}** pulls the trigger... **And dies!**`);
winner = notPlayer;
} else {
await msg.say(stripIndents`
**${player.tag}** pulls the trigger... **And lives...**
${opponent.bot ? 'Continue?' : `Will you take the gun, ${notPlayer}?`} (${8 - round - 1} shots left)
`);
const keepGoing = await verify(msg.channel, opponent.bot ? msg.author : notPlayer);
if (!keepGoing) winner = notPlayer;
round++;
userTurn = !userTurn;
}
}
this.client.games.delete(msg.channel.id);
return msg.say(`The winner is ${winner}!`);
} catch (err) {
this.client.games.delete(msg.channel.id);
throw err;
}
}
};
+90
View File
@@ -0,0 +1,90 @@
const Command = require('../../structures/Command');
const { stripIndents } = require('common-tags');
const { verify } = require('../../util/Util');
module.exports = class TicTacToeCommand extends Command {
constructor(client) {
super(client, {
name: 'tic-tac-toe',
group: 'mp-games',
memberName: 'tic-tac-toe',
description: 'Play a game of tic-tac-toe with another user.',
guildOnly: true,
args: [
{
key: 'opponent',
prompt: 'What user would you like to challenge?',
type: 'user'
}
]
});
}
async run(msg, { opponent }) {
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.');
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 sides = ['0', '1', '2', '3', '4', '5', '6', '7', '8'];
const taken = [];
let userTurn = true;
let winner = null;
while (!winner && taken.length < 9) {
const user = userTurn ? msg.author : opponent;
const sign = userTurn ? 'X' : 'O';
await msg.say(stripIndents`
${user}, which side do you pick?
\`\`\`
${sides[0]} | ${sides[1]} | ${sides[2]}
—————————
${sides[3]} | ${sides[4]} | ${sides[5]}
—————————
${sides[6]} | ${sides[7]} | ${sides[8]}
\`\`\`
`);
const filter = res => {
const choice = res.content;
return res.author.id === user.id && sides.includes(choice) && !taken.includes(choice);
};
const turn = await msg.channel.awaitMessages(filter, {
max: 1,
time: 30000
});
if (!turn.size) {
await msg.say('Sorry, time is up!');
userTurn = !userTurn;
continue;
}
const choice = turn.first().content;
sides[Number.parseInt(choice, 10)] = sign;
taken.push(choice);
if (this.verifyWin(sides)) winner = userTurn ? msg.author : opponent;
userTurn = !userTurn;
}
this.client.games.delete(msg.channel.id);
return msg.say(winner ? `Congrats, ${winner}!` : 'Oh... The cat won.');
} catch (err) {
this.client.games.delete(msg.channel.id);
throw err;
}
}
verifyWin(sides) {
return (sides[0] === sides[1] && sides[0] === sides[2])
|| (sides[0] === sides[3] && sides[0] === sides[6])
|| (sides[3] === sides[4] && sides[3] === sides[5])
|| (sides[1] === sides[4] && sides[1] === sides[7])
|| (sides[6] === sides[7] && sides[6] === sides[8])
|| (sides[2] === sides[5] && sides[2] === sides[8])
|| (sides[0] === sides[4] && sides[0] === sides[8])
|| (sides[2] === sides[4] && sides[2] === sides[6]);
}
};
+121
View File
@@ -0,0 +1,121 @@
const Command = require('../../structures/Command');
const request = require('node-superfetch');
const { stripIndents } = require('common-tags');
const { delay, verify } = require('../../util/Util');
const startWords = require('../../assets/json/word-list');
const { WEBSTER_KEY } = process.env;
module.exports = class WordChainCommand extends Command {
constructor(client) {
super(client, {
name: 'word-chain',
group: 'mp-games',
memberName: 'word-chain',
description: 'Try to come up with words that start with the last letter of your opponent\'s word.',
guildOnly: true,
credit: [
{
name: 'Moby Word Lists by Grady Ward',
url: 'http://www.gutenberg.org/ebooks/3201'
}
],
args: [
{
key: 'opponent',
prompt: 'What user would you like to challenge?',
type: 'user'
},
{
key: 'time',
prompt: 'How long do you want to wait for input of new words (in seconds)?',
type: 'integer',
default: 10,
max: 10,
min: 1
}
]
});
}
async run(msg, { opponent, time }) {
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.');
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 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 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;
}
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;
}
}
async verifyWord(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 false;
return true;
} catch (err) {
if (err.status === 404) return false;
return null;
}
}
};