Mafia Command

This commit is contained in:
Dragon Fire
2021-03-14 15:03:08 -04:00
parent fbcd5508cb
commit 55cb083d06
64 changed files with 239 additions and 1 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+86
View File
@@ -0,0 +1,86 @@
const Command = require('../../structures/Command');
const Game = require('../../structures/mafia/Game');
const { verify } = require('../../util/Util');
const storyCount = 21;
module.exports = class MafiaCommand extends Command {
constructor(client) {
super(client, {
name: 'mafia',
group: 'mafia',
memberName: 'mafia',
description: 'Who is the Mafia? Who is the detective? Will the Mafia kill them all?',
guildOnly: 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.voice.connections.get(msg.guild.id);
if (!connection) {
const usage = this.client.registry.commands.get('join').usage();
return msg.reply(`I am not in a voice channel. Use ${usage} to fix that!`);
}
for (const member of connection.channel.members.values()) await msg.guild.members.fetch(member.id);
if (connection.channel.members.size > 16) return msg.reply('Please do not have more than 15 players.');
if (connection.channel.members.size < 7) return msg.reply('Please have at least 5 players 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;
}
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!`);
}
}
};
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "xiao", "name": "xiao",
"version": "132.5.0", "version": "132.6.0",
"description": "Your personal server companion.", "description": "Your personal server companion.",
"main": "Xiao.js", "main": "Xiao.js",
"scripts": { "scripts": {
+109
View File
@@ -0,0 +1,109 @@
const Collection = require('@discordjs/collection');
const path = require('path');
const { stripIndents } = require('common-tags');
const Player = require('./Player');
const { shuffle } = require('../../util/Util');
const { SUCCESS_EMOJI_ID } = process.env;
module.exports = class Game {
constructor(client, channel, connection) {
Object.defineProperty(this, 'client', { value: client });
this.name = 'mafia';
this.players = new Collection();
this.channel = channel;
this.connection = connection;
this.turn = 1;
}
determineRoles(playerCount) {
const roles = ['detective', 'mafia', 'mafia'];
for (let i = 0; i < (playerCount - 3); i++) roles.push('innocent');
return shuffle(roles);
}
async generate(list) {
const roles = this.determineRoles(list.length);
let i = 0;
for (const user of list) {
try {
await user.send(`You are ${roles[i] === 'detective' ? 'the' : 'a part of the'} **${roles[i]}**.`);
} catch (err) {
await this.channel.send(
`${user}, I couldn't send a DM to you. Please open your DMs and use the \`me\` command to see your role.`
);
}
const player = new Player(this, user, roles[i]);
this.players.set(user.id, player);
i++;
}
return this.players;
}
playAudio(id) {
this.dispatcher = this.connection.play(
path.join(__dirname, '..', '..', 'assets', 'sounds', 'mafia', `${id}.mp3`), { volume: 2 }
);
return new Promise((res, rej) => {
this.dispatcher.once('finish', () => {
this.dispatcher = null;
return res(true);
});
this.dispatcher.once('error', err => {
this.dispatcher = null;
return rej(err);
});
});
}
async getVotes(playersArr) {
await this.channel.send(stripIndents`
Who do you think is a Mafia member? Please type the number.
${playersArr.map((p, i) => `**${i + 1}.** ${p.user.tag}`).join('\n')}
`);
const voted = [];
const filter = res => {
if (!this.players.some(p => p.user.id === res.author.id)) return false;
if (voted.includes(res.author.id)) return false;
if (!playersArr[Number.parseInt(res.content, 10) - 1]) return false;
voted.push(res.author.id);
res.react(SUCCESS_EMOJI_ID || '✅').catch(() => null);
return true;
};
const votes = await this.channel.awaitMessages(filter, {
max: this.players.size,
time: 90000
});
if (!votes.size) return null;
return votes;
}
getHanged(votes, playersArr) {
const counts = new Collection();
for (const vote of votes.values()) {
const player = this.players.get(playersArr[Number.parseInt(vote.content, 10) - 1].id);
if (counts.has(player.id)) {
++counts.get(player.id).votes;
} else {
counts.set(player.id, {
id: player.id,
votes: 1,
user: player.user
});
}
}
return counts.sort((a, b) => b.votes - a.votes).first();
}
get shouldEnd() {
return this.players.size < 4 && !this.players.some(p => p.role === 'mafia');
}
get voiceChannel() {
return this.connection.channel;
}
get dispatcher() {
return this.client.dispatchers.get(this.channel.guild.id);
}
};
+43
View File
@@ -0,0 +1,43 @@
const { stripIndents } = require('common-tags');
const questions = {
mafia: 'Who would you like to kill?',
detective: 'Who do you think is a Mafia member?'
};
module.exports = class Player {
constructor(game, user, role) {
this.game = game;
this.user = user;
this.id = user.id;
this.role = role;
}
toString() {
return this.user.toString();
}
async dmRound() {
const valid = Array.from(this.game.players.filter(p => p.role !== this.role).values());
await this.user.send(stripIndents`
${questions[this.role]} Please type the number.
${valid.map((p, i) => `**${i + 1}.** ${p.user.tag}`).join('\n')}
`);
const filter = res => valid[Number.parseInt(res.content, 10) - 1];
const decision = await this.user.dmChannel.awaitMessages(filter, {
max: 1,
time: 120000
});
if (!decision.size) {
await this.user.send('Sorry, time is up!');
return null;
}
const choice = valid[Number.parseInt(decision.first().content, 10) - 1].id;
if (this.role === 'detective') {
const isMafia = this.game.players.get(choice).role === 'mafia';
await this.user.send(isMafia ? 'Yes, they are a Mafioso.' : 'No, they are not a Mafioso.');
} else {
await this.user.send(`**${this.game.players.get(choice).user.tag}** is your choice...`);
}
return choice;
}
};